/// <summary>
        /// Handles one message.
        /// </summary>
        /// <param name="message">The message.</param>
        private void HandleOneMessage(RtspMessage message)
        {
            Contract.Requires(message != null);

            RtspListener destination = null;

            if (message is RtspRequest)
            {
                destination = HandleRequest(ref message);
                _logger.Debug("Dispatch message from {0} to {1}",
                              message.SourcePort != null ? message.SourcePort.RemoteAdress : "UNKNOWN", destination != null ? destination.RemoteAdress : "UNKNOWN");

                // HandleRequest can change message type.
                if (message is RtspRequest)
                {
                    var context = new OriginContext();
                    context.OriginCSeq                   = message.CSeq;
                    context.OriginSourcePort             = message.SourcePort;
                    (message as RtspRequest).ContextData = context;
                }
            }
            else if (message is RtspResponse)
            {
                RtspResponse response = message as RtspResponse;

                if (response.OriginalRequest != null)
                {
                    var context = response.OriginalRequest.ContextData as OriginContext;
                    if (context != null)
                    {
                        destination   = context.OriginSourcePort;
                        response.CSeq = context.OriginCSeq;
                        _logger.Debug("Dispatch response back to {0}", destination.RemoteAdress);
                    }
                }

                HandleResponse(response);
            }

            if (destination != null)
            {
                bool isGood = destination.SendMessage(message);

                if (!isGood)
                {
                    destination.Stop();
                    _serverListener.Remove(destination.RemoteAdress);

                    // send back a message because we can't forward.
                    if (message is RtspRequest && message.SourcePort != null)
                    {
                        RtspRequest  request           = message as RtspRequest;
                        RtspResponse theDirectResponse = request.CreateResponse();
                        _logger.Warn("Error during forward : {0}. So sending back a direct error response", message.Command);
                        theDirectResponse.ReturnCode = 500;
                        request.SourcePort.SendMessage(theDirectResponse);
                    }
                }
            }
        }
Example #2
0
        public void UpdatePushedMessages(RtspMessage request, RtspMessage response)
        {
            if (false.Equals(Common.IDisposedExtensions.IsNullOrDisposed(request)) && false.Equals(Common.IDisposedExtensions.IsNullOrDisposed(LastInboundRequest)))
            {
                LastInboundRequest.IsPersistent = false;

                LastInboundRequest.Dispose();
            }


            if (Common.IDisposedExtensions.IsNullOrDisposed(LastInboundRequest = request).Equals(false))
            {
                LastInboundRequest.IsPersistent = true;
            }

            if (false.Equals(Common.IDisposedExtensions.IsNullOrDisposed(LastInboundResponse)))
            {
                LastInboundResponse.IsPersistent = false;

                LastInboundResponse.Dispose();
            }

            if (Common.IDisposedExtensions.IsNullOrDisposed(LastInboundResponse = response).Equals(false))
            {
                LastInboundResponse.IsPersistent = true;
            }
        }
Example #3
0
        public void UpdateMessages(RtspMessage request, RtspMessage response)
        {
            if (IDisposedExtensions.IsNullOrDisposed(request).Equals(false) &&
                IDisposedExtensions.IsNullOrDisposed(LastRequest).Equals(false))
            {
                LastRequest.IsPersistent = false;

                LastRequest.Dispose();
            }

            if (IDisposedExtensions.IsNullOrDisposed(LastRequest = request).Equals(false))
            {
                LastRequest.IsPersistent = true;
            }

            if (IDisposedExtensions.IsNullOrDisposed(LastResponse).Equals(false))
            {
                LastResponse.IsPersistent = false;

                LastResponse.Dispose();
            }

            if (IDisposedExtensions.IsNullOrDisposed(LastResponse = response).Equals(false))
            {
                LastResponse.IsPersistent = true;
            }
        }
Example #4
0
        public bool ParseSessionIdAndTimeout(RtspMessage from)
        {
            SessionId = from[RtspHeaders.Session];

            Timeout = System.TimeSpan.FromSeconds(60);//Default

            //If there is a session header it may contain the option timeout
            if (false == string.IsNullOrWhiteSpace(SessionId))
            {
                //Check for session and timeout

                //Get the values
                string[] sessionHeaderParts = SessionId.Split(RtspHeaders.SemiColon);

                int headerPartsLength = sessionHeaderParts.Length;

                //Check if a valid value was given
                if (headerPartsLength > 0)
                {
                    //Trim it of whitespace
                    string value = System.Linq.Enumerable.LastOrDefault(sessionHeaderParts, (p => false == string.IsNullOrWhiteSpace(p)));

                    //If we dont have an exiting id then this is valid if the header was completely recieved only.
                    if (false == string.IsNullOrWhiteSpace(value) &&
                        true == string.IsNullOrWhiteSpace(SessionId) ||
                        value[0] != SessionId[0])
                    {
                        //Get the SessionId if present
                        SessionId = sessionHeaderParts[0].Trim();

                        //Check for a timeout
                        if (sessionHeaderParts.Length > 1)
                        {
                            string timeoutPart = sessionHeaderParts[1];

                            if (false == string.IsNullOrWhiteSpace(timeoutPart))
                            {
                                int timeoutStart = 1 + timeoutPart.IndexOf(Media.Sdp.SessionDescription.EqualsSign);

                                if (timeoutStart >= 0 && int.TryParse(timeoutPart.Substring(timeoutStart), out timeoutStart))
                                {
                                    if (timeoutStart > 0)
                                    {
                                        Timeout = System.TimeSpan.FromSeconds(timeoutStart);
                                    }
                                }
                            }
                        }

                        value = null;
                    }
                }

                sessionHeaderParts = null;

                return(true);
            }

            return(false);
        }
        private static RtspListener HandleRequestWithoutUrl(ref RtspMessage message)
        {
            Contract.Requires(message != null);
            Contract.Requires(message is RtspRequest);
            Contract.Ensures(Contract.Result <RtspListener>() != null);
            Contract.Ensures(Contract.ValueAtReturn(out message) != null);


            RtspListener destination;

            destination = message.SourcePort;
            RtspRequest  request           = message as RtspRequest;
            RtspResponse theDirectResponse = request.CreateResponse();

            if (request.RequestTyped == RtspRequest.RequestType.OPTIONS)
            {
                // We know what to do...
                theDirectResponse.ReturnCode = 200;
                // But perhaps it is to prevent timeout !!
                // ARG .....
                _logger.Info("I got an OPTION * message, I reply but I do not forward! The end session may timeout.");
                request.LogMessage();
            }
            else
            {
                _logger.Warn("Do not know how to handle : {0}", message.Command);
                theDirectResponse.ReturnCode = 400;
            }
            message = theDirectResponse;
            return(destination);
        }
Example #6
0
 public RtspSession(RtspMessage request, RtspMessage response)
     : this(response)
 {
     if (request != null)
     {
         LastRequest = request;
     }
 }
Example #7
0
 public RtspSession(RtspMessage response, bool shouldDispose = true)
     : base(shouldDispose)
 {
     if (response != null)
     {
         ParseSessionIdAndTimeout(LastResponse = response);
     }
 }
Example #8
0
        public RtspSession(RtspMessage response)
        {
            if (response != null)
            {
                LastResponse = response;

                ParseSessionIdAndTimeout(LastResponse);
            }
        }
Example #9
0
        /// <summary>
        /// Sends the message.
        /// </summary>
        /// <param name="message">A message.</param>
        /// <returns><see cref="true"/> if it is Ok, otherwise <see cref="false"/></returns>
        public bool SendMessage(RtspMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            Contract.EndContractBlock();

            if (!_transport.Connected)
            {
                if (!AutoReconnect)
                {
                    return(false);
                }

                _logger.Warn($"{_sourceId} Reconnect to a client, strange !!");
                try
                {
                    Reconnect();
                }
                catch (SocketException)
                {
                    // on a pas put se connecter on dit au manager de plus compter sur nous
                    return(false);
                }
            }

            // if it it a request  we store the original message
            // and we renumber it.
            //TODO handle lost message (for example every minute cleanup old message)
            if (message is RtspRequest)
            {
                RtspMessage originalMessage = message;
                // Do not modify original message
                message = message.Clone() as RtspMessage;
                _sequenceNumber++;
                message.CSeq = _sequenceNumber;
                lock (_sentMessage)
                {
                    _sentMessage.Add(message.CSeq, originalMessage as RtspRequest);
                }
            }

            try
            {
                _logger.Debug($"{_sourceId} Send Message");
                message.LogMessage(_logger);
                message.SendTo(_stream);
            }
            catch (Exception ex)
            {
                _logger.Debug($"{_sourceId} Error Send Message {ex}");
                return(false);
            }

            return(true);
        }
        public void GetRtspMessageRequest(string requestLine, RtspRequest.RequestType requestType)
        {
            RtspMessage oneMessage = RtspMessage.GetRtspMessage(requestLine);

            Assert.IsInstanceOfType(typeof(RtspRequest), oneMessage);

            RtspRequest oneRequest = oneMessage as RtspRequest;

            Assert.AreEqual(requestType, oneRequest.RequestTyped);
        }
Example #11
0
        // Generate Basic or Digest Authorization
        public void AddAuthorization(RtspMessage message, string username, string password,
                                     string auth_type, string realm, string nonce, string url)
        {
            if (username == null || username.Length == 0)
            {
                return;
            }
            if (password == null || password.Length == 0)
            {
                return;
            }
            if (realm == null || realm.Length == 0)
            {
                return;
            }
            if (auth_type.Equals("Digest") && (nonce == null || nonce.Length == 0))
            {
                return;
            }

            if (auth_type.Equals("Basic"))
            {
                byte[] credentials         = System.Text.Encoding.UTF8.GetBytes(username + ":" + password);
                String credentials_base64  = Convert.ToBase64String(credentials);
                String basic_authorization = "Basic " + credentials_base64;

                message.Headers.Add(RtspHeaderNames.Authorization, basic_authorization);

                return;
            }
            else if (auth_type.Equals("Digest"))
            {
                string method = message.Method;                 // DESCRIBE, SETUP, PLAY etc

                MD5    md5      = System.Security.Cryptography.MD5.Create();
                String hashA1   = CalculateMD5Hash(md5, username + ":" + realm + ":" + password);
                String hashA2   = CalculateMD5Hash(md5, method + ":" + url);
                String response = CalculateMD5Hash(md5, hashA1 + ":" + nonce + ":" + hashA2);

                const String quote = "\"";
                String       digest_authorization = "Digest username="******", "
                                                    + "realm=" + quote + realm + quote + ", "
                                                    + "nonce=" + quote + nonce + quote + ", "
                                                    + "uri=" + quote + url + quote + ", "
                                                    + "response=" + quote + response + quote;

                message.Headers.Add(RtspHeaderNames.Authorization, digest_authorization);

                return;
            }
            else
            {
                return;
            }
        }
        public void GetRtspMessageResponse(string requestLine, int returnCode, string returnMessage)
        {
            RtspMessage oneMessage = RtspMessage.GetRtspMessage(requestLine);

            Assert.IsInstanceOfType(typeof(RtspResponse), oneMessage);

            RtspResponse oneResponse = oneMessage as RtspResponse;

            Assert.AreEqual(returnCode, oneResponse.ReturnCode);
            Assert.AreEqual(returnMessage, oneResponse.ReturnMessage);
        }
 internal override void LogRequest(RtspMessage request, ClientSession session)
 {
     ConsoleColor previous = Console.ForegroundColor;
     try
     {
         Console.ForegroundColor = RequestColor;
         Logger.Log(string.Format(Format, "Request=>", request, "Session=>", session.Id, null));
     }
     catch { throw; }
     finally { Console.ForegroundColor = previous; }
 }
Example #14
0
        internal override void LogResponse(RtspMessage response, ClientSession session)
        {
            ConsoleColor previous = Console.ForegroundColor;

            try
            {
                Console.ForegroundColor = ResponseColor;
                Logger.Log(string.Format(Format, "Response=>", response, "Session=>", session.Id, null));
            }
            catch { throw; }
            finally { Console.ForegroundColor = previous; }
        }
Example #15
0
        // Generate Basic or Digest Authorization
        public void AddAuthorization(RtspMessage message, string username, string password, string auth_type, string realm, string nonce, string url)
        {
            if (string.IsNullOrEmpty(username))
            {
                return;
            }
            if (string.IsNullOrEmpty(password))
            {
                return;
            }
            if (string.IsNullOrEmpty(realm))
            {
                return;
            }
            if (auth_type.Equals("Digest") && (string.IsNullOrEmpty(nonce)))
            {
                return;
            }

            if (auth_type.Equals("Basic"))
            {
                byte [] credentials         = System.Text.Encoding.UTF8.GetBytes(username + ":" + password);
                string  credentials_base64  = Convert.ToBase64String(credentials);
                string  basic_authorization = "Basic " + credentials_base64;

                message.Headers.Add(RtspHeaderNames.Authorization, basic_authorization);

                return;
            }

            if (auth_type.Equals("Digest"))
            {
                var    md5      = MD5.Create();
                string method   = message.Method;               // DESCRIBE, SETUP, PLAY etc
                string hashA1   = CalculateMD5Hash(md5, username + ":" + realm + ":" + password);
                string hashA2   = CalculateMD5Hash(md5, method + ":" + url);
                string response = CalculateMD5Hash(md5, hashA1 + ":" + nonce + ":" + hashA2);

                const string quote = "\"";
                string       digest_authorization = "Digest username="******", "
                                                    + "realm=" + quote + realm + quote + ", "
                                                    + "nonce=" + quote + nonce + quote + ", "
                                                    + "uri=" + quote + url + quote + ", "
                                                    + "response=" + quote + response + quote;

                message.Headers.Add(RtspHeaderNames.Authorization, digest_authorization);
            }
        }
Example #16
0
        /// <summary>
        /// Sends the message.
        /// </summary>
        /// <param name="message">A message.</param>
        /// <returns><see cref="true"/> if it is Ok, otherwise <see cref="false"/></returns>
        public bool SendMessage(RtspMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            Contract.EndContractBlock();

            if (!_transport.Connected)
            {
                if (!AutoReconnect)
                {
                    return(false);
                }

                _logger.Warn("Reconnect to a client, strange !!");
                try
                {
                    Reconnect();
                }
                catch (SocketException)
                {
                    // on a pas put se connecter on dit au manager de plus compter sur nous
                    return(false);
                }
            }

            // if it it a request  we store the original message
            // and we renumber it.
            //TODO handle lost message (for example every minute cleanup old message)
            if (message is RtspRequest requestMsg)
            {
                // Original message has CSeq set. Make it so.
                message.CSeq = ++_sequenceNumber;
                RtspMessage originalMessage = message.Clone() as RtspMessage;
                ((RtspRequest)originalMessage).ContextData = requestMsg.ContextData;

                lock (_sentMessage)
                {
                    _sentMessage.Add(message.CSeq, originalMessage as RtspRequest);
                }
            }

            _logger.Info("Send Message");
            message.LogMessage();
            message.SendTo(_transport.GetStream());
            return(true);
        }
Example #17
0
        private RtspListener HandleRequestPush(ref RtspMessage message)
        {
            Contract.Requires(message != null);
            Contract.Requires(message is RtspRequest);
            Contract.Ensures(Contract.Result <RtspListener>() != null);
            Contract.Ensures(Contract.ValueAtReturn(out message) != null);


            RtspListener destination;

            destination = message.SourcePort;
            RtspRequest  request = message as RtspRequest;
            RtspResponse theDirectResponse;



            switch (request.RequestTyped)
            {
            case RtspRequest.RequestType.OPTIONS:
                theDirectResponse = pushManager.HandleOptions(message as RtspRequestOptions);
                break;

            case RtspRequest.RequestType.ANNOUNCE:
                theDirectResponse = pushManager.HandleAnnounce(message as RtspRequestAnnounce);
                break;

            case RtspRequest.RequestType.SETUP:
                theDirectResponse = pushManager.HandleSetup(message as RtspRequestSetup);
                break;

            case RtspRequest.RequestType.RECORD:
                theDirectResponse = pushManager.HandleRecord(message as RtspRequestRecord);
                break;

            case RtspRequest.RequestType.TEARDOWN:
                theDirectResponse = pushManager.HandleTeardown(message as RtspRequestTeardown);
                break;

            default:
                _logger.Warn("Do not know how to handle : {0}", message.Command);
                theDirectResponse            = request.CreateResponse();
                theDirectResponse.ReturnCode = 400;
                break;
            }

            message = theDirectResponse;
            return(destination);
        }
Example #18
0
        private RtspListener HandleRequestPull(ref RtspMessage message)
        {
            Contract.Requires(message != null);
            Contract.Requires(message is RtspRequest);
            Contract.Ensures(Contract.Result <RtspListener>() != null);
            Contract.Ensures(Contract.ValueAtReturn(out message) != null);


            RtspListener destination;

            destination = message.SourcePort;
            RtspRequest  request = message as RtspRequest;
            RtspResponse theDirectResponse;



            switch (request.RequestTyped)
            {
            case RtspRequest.RequestType.OPTIONS:
                theDirectResponse = pushManager.HandleOptions(message as RtspRequestOptions);
                break;

            case RtspRequest.RequestType.DESCRIBE:
                theDirectResponse = pushManager.HandlePullDescribe(message as RtspRequestDescribe);
                break;

            case RtspRequest.RequestType.SETUP:
                theDirectResponse = pushManager.HandlePullSetup(message as RtspRequestSetup);
                break;

            case RtspRequest.RequestType.PLAY:
                theDirectResponse = pushManager.HandlePullPlay(message as RtspRequestPlay);
                break;

            case RtspRequest.RequestType.GET_PARAMETER:
                theDirectResponse = pushManager.HandlePullGetParameter(message as RtspRequestGetParameter);
                break;

            default:
                _logger.Warn("Do not know how to handle : {0}", message.Command);
                theDirectResponse            = request.CreateResponse();
                theDirectResponse.ReturnCode = 400;
                break;
            }

            message = theDirectResponse;
            return(destination);
        }
Example #19
0
        public void ParseDelay(RtspMessage from)
        {
            //Determine if delay was honored.
            string timestampHeader = from.GetHeader(RtspHeaders.Timestamp);

            //If there was a Timestamp header
            if (false == string.IsNullOrWhiteSpace(timestampHeader))
            {
                timestampHeader = timestampHeader.Trim();

                //check for the delay token
                int indexOfDelay = timestampHeader.IndexOf("delay=");

                //if present
                if (indexOfDelay >= 0)
                {
                    //attempt to calculate it from the given value
                    double delay = double.NaN;

                    if (double.TryParse(timestampHeader.Substring(indexOfDelay + 6).TrimEnd(), out delay))
                    {
                        //Set the value of the servers delay
                        LastServerDelay = System.TimeSpan.FromSeconds(delay);

                        //Could add it to the existing SocketReadTimeout and SocketWriteTimeout.
                    }
                }
                else
                {
                    //MS servers don't use a ; to indicate delay
                    string[] parts = timestampHeader.Split(RtspMessage.SpaceSplit, 2);

                    //If there was something after the space
                    if (parts.Length > 1)
                    {
                        //attempt to calulcate it from the given value
                        double delay = double.NaN;

                        if (double.TryParse(parts[1].Trim(), out delay))
                        {
                            //Set the value of the servers delay
                            LastServerDelay = System.TimeSpan.FromSeconds(delay);
                        }
                    }
                }
            }
        }
Example #20
0
        void sender_OnRequest(RtspClient sender, RtspMessage request)
        {
            if (request != null)
            {
                //Disable keep alives if indicated
                Client.DisableKeepAliveRequest = checkBox1.Checked;

                if (this.InvokeRequired)
                {
                    Invoke(new FillTextRtsp(UpdateRtsp), request);
                }
                else
                {
                    textBox2.AppendText(string.Format(Media.UnitTests.Program.TestingFormat, request.ToString()));
                }
            }
        }
Example #21
0
        private void RTSPClient_OnRequest(RtspClient sender, RtspMessage request)
        {
            if (InvokeRequired)
            {
                MethodInvoker del = delegate { RTSPClient_OnRequest(sender, request); };
                Invoke(del);
            }
            else
            {
                if (request != null)
                {
                    if (m_rtspClient == null || m_rtspClient.IsDisposed)
                    {
                        return;
                    }

                    //Disable keep alives if indicated
                    //m_rtspClient.DisableKeepAliveRequest = checkBox1.Checked;

//					RTSPMessagesTextBox.AppendText("RTSPClient_OnRequest" + Environment.NewLine);
//					RTSPMessagesTextBox.AppendText("@" + request.Created.ToUniversalTime().ToString() + " - " + request.ToString() + Environment.NewLine);

                    if (m_rtspClient.IsPlaying)
                    {
                        PlayButton.Text = "(Playing)STOP";

                        if (!m_rtspClient.LivePlay)
                        {
                            label2.Text = "Remaining: " + (DateTime.UtcNow - m_rtspClient.StartedPlaying.Value).Subtract(m_rtspClient.EndTime.Value).ToString();
                        }
                    }
                    else
                    {
                        PlayButton.Text = "STOP";
                        if (m_rtspClient.LivePlay)
                        {
                            label2.Text = "Live Play";
                        }
                        else
                        {
                            label2.Text = "Done Playing";
                        }
                    }
                }
            }
        }
Example #22
0
        /// <summary>
        /// Sends the message.
        /// </summary>
        /// <param name="message">A message.</param>
        /// <returns><see cref="true"/> if it is Ok, otherwise <see cref="false"/></returns>
        public bool SendMessage(RtspMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
//            Contract.EndContractBlock();

            if (!_transport.Connected)
            {
//                _logger.Warn("Reconnect to a client, strange !!");
                try
                {
                    Reconnect();
                }
                catch (SocketException)
                {
                    // on a pas put se connecter on dit au manager de plus compter sur nous
                    return(false);
                }
            }

            // if it it a request  we store the original message
            // and we renumber it.
            if (message is RtspRequest)
            {
                RtspMessage originalMessage = message;
                // Do not modify original message
                message = message.Clone() as RtspMessage;
                _sequenceNumber++;
                message.CSeq = _sequenceNumber;
                lock (_sentMessage)
                {
                    _sentMessage.Add(message.CSeq, originalMessage as RtspRequest);
                }
            }

//            _logger.Debug("Send Message");
            message.LogMessage();
            message.SendTo(_stream);
            return(true);
        }
Example #23
0
        private void RTSPClient_OnResponse(RtspClient sender, RtspMessage request, RtspMessage response)
        {
            if (InvokeRequired)
            {
                MethodInvoker del = delegate { RTSPClient_OnResponse(sender, request, response); };
                Invoke(del);
            }
            else
            {
                if (request != null && response != null)
                {
                    //Disable keep alives if indicated
                    //m_rtspClient.DisableKeepAliveRequest = checkBox1.Checked;

                    RTSPMessagesTextBox.AppendText("RTSPClient_OnResponse (Request)" + Environment.NewLine);
                    RTSPMessagesTextBox.AppendText("@" + request.Created.ToUniversalTime().ToString() + " - " + request.ToString() + Environment.NewLine);

                    RTSPMessagesTextBox.AppendText("RTSPClient_OnResponse (Response)" + Environment.NewLine);
                    RTSPMessagesTextBox.AppendText("@" + response.Created.ToUniversalTime().ToString() + " - " + response.ToString() + Environment.NewLine);
                }
            }
        }
Example #24
0
        public void UpdatePushedMessages(RtspMessage request, RtspMessage response)
        {
            if (request != null && LastInboundRequest != null)
            {
                LastInboundRequest.IsPersistent = false;

                LastInboundRequest.Dispose();
            }

            LastInboundRequest = request;

            LastInboundRequest.IsPersistent = true;

            if (LastInboundResponse != null)
            {
                LastInboundResponse.IsPersistent = false;

                LastInboundResponse.Dispose();
            }

            LastInboundResponse = response;

            LastInboundResponse.IsPersistent = true;
        }
Example #25
0
 internal override void LogRequest(RtspMessage request, ClientSession session)
 {
     try { Logger.Log(string.Format(Format, request.RtspMessageType, request.RtspMethod, request.Location, session.Id, null)); }
     catch { throw; }
 }
Example #26
0
        internal virtual void ProcessAuthorizationRequired(IMedia source, ClientSession session, bool sendResponse = true)
        {
            RtspMessage response = new RtspMessage(RtspMessageType.Response);

            response.CSeq = session.LastRequest.CSeq;

            string authHeader = session.LastRequest.GetHeader(RtspHeaders.Authorization);

            RtspStatusCode statusCode;

            bool noAuthHeader = string.IsNullOrWhiteSpace(authHeader);

            if (noAuthHeader && session.LastRequest.ContainsHeader(RtspHeaders.AcceptCredentials)) statusCode = RtspStatusCode.ConnectionAuthorizationRequired;
            //If the last request did not have an authorization header
            else if (noAuthHeader)
            {
                /* -- http://tools.ietf.org/html/rfc2617

            qop
             Indicates what "quality of protection" the client has applied to
             the message. If present, its value MUST be one of the alternatives
             the server indicated it supports in the WWW-Authenticate header.
             These values affect the computation of the request-digest. Note
             that this is a single token, not a quoted list of alternatives as
             in WWW- Authenticate.  This directive is optional in order to
             preserve backward compatibility with a minimal implementation of
             RFC 2617 [6], but SHOULD be used if the server indicated that qop
             is supported by providing a qop directive in the WWW-Authenticate
             header field.

               cnonce
             This MUST be specified if a qop directive is sent (see above), and
             MUST NOT be specified if the server did not send a qop directive in
             the WWW-Authenticate header field.  The cnonce-value is an opaque
             quoted string value provided by the client and used by both client
             and server to avoid chosen plaintext attacks, to provide mutual
             authentication, and to provide some message integrity protection.
             See the descriptions below of the calculation of the response-
             digest and request-digest values.

                 */

                //Could retrieve values from last Request if needed..
                //string realm = "//", nOnceCount = "00000001";

                //Should handle multiple types of auth

                //Should store the nonce and cnonce values on the session
                statusCode = RtspStatusCode.Unauthorized;

                NetworkCredential requiredCredential = null;

                string authenticateHeader = null;

                Uri relativeLocation = source.ServerLocation;

                //Check for Digest first - Todo Finish implementation
                if ((requiredCredential = RequiredCredentials.GetCredential(relativeLocation, "Digest")) != null)
                {
                    //Might need to store values qop nc, cnonce and nonce in session storage for later retrival

                    //Should use auth-int and qop

                    authenticateHeader = string.Format(System.Globalization.CultureInfo.InvariantCulture, "Digest username={0},realm={1},nonce={2},cnonce={3}", requiredCredential.UserName, (string.IsNullOrWhiteSpace(requiredCredential.Domain) ? ServerName : requiredCredential.Domain), ((long)(Utility.Random.Next(int.MaxValue) << 32 | (Utility.Random.Next(int.MaxValue)))).ToString("X"), Utility.Random.Next(int.MaxValue).ToString("X"));
                }
                else if ((requiredCredential = RequiredCredentials.GetCredential(relativeLocation, "Basic")) != null)
                {
                    authenticateHeader = "Basic realm=\"" + (string.IsNullOrWhiteSpace(requiredCredential.Domain) ? ServerName : requiredCredential.Domain + '"');
                }

                if (!string.IsNullOrWhiteSpace(authenticateHeader))
                {
                    response.SetHeader(RtspHeaders.WWWAuthenticate, authenticateHeader);
                }
            }
            else //Authorization header was present but data was incorrect
            {
                //Parse type from authHeader

                string[] parts = authHeader.Split((char)Common.ASCII.Space);

                string authType = null;

                if (parts.Length > 0)
                {
                    authType = parts[0];
                    authHeader = parts[1];
                }

                //should check to ensure wrong type was not used e.g. basic in place of digest...
                if (string.Compare(authType, "digest", true) == 0)
                {
                    //if (session.Storage["nOnce"] != null)
                    //{
                    //    //Increment NonceCount
                    //}
                }

                //Increment session attempts?

                statusCode = RtspStatusCode.Forbidden;
            }

            //Set the status code
            response.StatusCode = statusCode;

            //Send the response
            ProcessInvalidRtspRequest(response, session, sendResponse);
        }
        /// <summary>
        /// Rewrites the SDP in the message.
        /// </summary>
        /// <param name="aMessage">A message.</param>
        private static void RewriteSDPMessage(RtspMessage aMessage)
        {
            //System.Text.Encoding.

            Encoding sdpEncoding = null;

            try
            {
                if (aMessage.Headers.ContainsKey(RtspHeaderNames.ContentEncoding))
                {
                    Encoding.GetEncoding(aMessage.Headers[RtspHeaderNames.ContentEncoding]);
                }
            }
            catch (ArgumentException)
            {
            }

            //fall back to UTF-8
            if (sdpEncoding == null)
            {
                sdpEncoding = Encoding.UTF8;
            }


            string sdpFile = sdpEncoding.GetString(aMessage.Data);

            using (StringReader readsdp = new StringReader(sdpFile))
            {
                StringBuilder newsdp = new StringBuilder();

                string line = readsdp.ReadLine();
                while (line != null)
                {
                    if (line.Contains("a=control:rtsp://"))
                    {
                        string[]   lineElement = line.Split(new char[] { ':' }, 2);
                        UriBuilder temp        = new UriBuilder(lineElement[1]);
                        temp.Path = temp.Host + ":" + temp.Port.ToString(CultureInfo.InvariantCulture) + temp.Path;

                        string domainName = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName;
                        string hostName   = Dns.GetHostName();
                        string fqdn       = String.Empty;
                        if (!hostName.Contains(domainName))
                        {
                            fqdn = hostName + "." + domainName;
                        }
                        else
                        {
                            fqdn = hostName;
                        }

                        temp.Host = fqdn;
                        temp.Port = 8554;
                        line      = lineElement[0] + ":" + temp.ToString();
                    }
                    if (line.Contains("c=IN IP4 "))
                    {
                        line = string.Format(CultureInfo.InvariantCulture, "c=IN IP4 {0}", CreateNextMulticastAddress());
                    }
                    newsdp.Append(line);
                    newsdp.Append("\r\n");
                    line = readsdp.ReadLine();
                }

                aMessage.Data = sdpEncoding.GetBytes(newsdp.ToString());
            }
            aMessage.AdjustContentLength();
        }
 /// <summary>
 /// Enqueues the specified message.
 /// </summary>
 /// <param name="message">The message.</param>
 public void Enqueue(RtspMessage message)
 {
     _logger.Debug("One message enqueued");
     _queue.Enqueue(message);
     _newMessage.Set();
 }
        /// <summary>
        /// Handles request message.
        /// </summary>
        /// <param name="message">A message, can be rewriten.</param>
        /// <returns>The destination</returns>
        private RtspListener HandleRequest(ref RtspMessage message)
        {
            Contract.Requires(message != null);
            Contract.Requires(message is RtspRequest);
            Contract.Ensures(Contract.Result <RtspListener>() != null);
            Contract.Ensures(Contract.ValueAtReturn(out message) != null);


            RtspRequest request = message as RtspRequest;

            RtspListener destination;

            // Do not forward, direct respond because we do not know where to send.
            if (request.RtspUri == null || request.RtspUri.AbsolutePath.Split(new char[] { '/' }, 3).Length < 3)
            {
                destination = HandleRequestWithoutUrl(ref message);
            }
            else
            {
                try
                {
                    // get the real destination
                    request.RtspUri = RewriteUri(request.RtspUri);
                    destination     = GetRtspListenerForDestination(request.RtspUri);

                    // Handle setup
                    RtspRequestSetup requestSetup = request as RtspRequestSetup;
                    if (requestSetup != null)
                    {
                        message = HandleRequestSetup(ref destination, requestSetup);
                    }

                    //Handle Play Reques
                    RtspRequestPlay requestPlay = request as RtspRequestPlay;
                    if (requestPlay != null)
                    {
                        message = HandleRequestPlay(ref destination, requestPlay);
                    }



                    //Update session state and handle special message
                    if (request.Session != null && request.RtspUri != null)
                    {
                        string sessionKey = RtspSession.GetSessionName(request.RtspUri, request.Session);
                        if (_activesSession.ContainsKey(sessionKey))
                        {
                            _activesSession[sessionKey].Handle(request);
                            switch (request.RequestTyped)
                            {
                            // start here to start early
                            //case RtspRequest.RequestType.PLAY:
                            // _activesSession[sessionKey].Start(request.SourcePort.RemoteAdress);
                            //   break;
                            case RtspRequest.RequestType.TEARDOWN:
                                _activesSession[sessionKey].Stop(request.SourcePort.RemoteAdress);
                                if (!_activesSession[sessionKey].IsNeeded)
                                {
                                    _activesSession.Remove(sessionKey);
                                }
                                else
                                {
                                    // system still need the server to send data do not send him the message.
                                    // reponds to client directly.
                                    destination = request.SourcePort;
                                    message     = request.CreateResponse();
                                }
                                break;
                            }
                        }
                        else
                        {
                            _logger.Warn("Command {0} for session {1} which was not found", request.RequestTyped, sessionKey);
                        }
                    }
                }
                catch (Exception error)
                {
                    _logger.Error("Error during handle of request", error);
                    destination = request.SourcePort;
                    RtspResponse theDirectResponse = request.CreateResponse();
                    theDirectResponse.ReturnCode = 500;
                    message = theDirectResponse;
                }
            }

            return(destination);
        }
Example #30
0
        internal void ProcessRedirect(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            var found = FindStreamByLocation(request.Location);

            if (found == null)
            {
                ProcessLocationNotFoundRtspRequest(session, sendResponse);
                return;
            }

            if (false == AuthenticateRequest(request, found))
            {
                ProcessAuthorizationRequired(found, session, sendResponse);
                return;
            }

            if (false == found.Ready)
            {
                ProcessInvalidRtspRequest(session, RtspStatusCode.PreconditionFailed, null, sendResponse);
                return;
            }

            using (var resp = session.CreateRtspResponse(request))
            {
                resp.Method = RtspMethod.REDIRECT;
                resp.AppendOrSetHeader(RtspHeaders.Location, "rtsp://" + session.LocalEndPoint.Address.ToString() + "/live/" + found.Id);
                ProcessSendRtspMessage(resp, session, sendResponse);
            }
        }
 internal abstract void LogRequest(RtspMessage request, ClientSession session);
Example #32
0
        /// <summary>
        /// Ends the client session
        /// </summary>
        /// <param name="request">The Teardown request</param>
        /// <param name="session">The session which recieved the request</param>
        internal void ProcessRtspTeardown(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            //If there was a location which was not a wildcard attempt to honor it.
            if (request.Location != RtspMessage.Wildcard)
            {

                RtpSource found = FindStreamByLocation(request.Location) as RtpSource;

                if (found == null)
                {
                    ProcessLocationNotFoundRtspRequest(session, sendResponse);
                    return;
                }

                if (false == AuthenticateRequest(request, found))
                {
                    ProcessAuthorizationRequired(found, session, sendResponse);
                    return;
                }

                //Send the response
                var resp = session.ProcessTeardown(request, found);

                ////Keep track if LeaveOpen should be set (based on if the session still shared the socket)
                //if (false == (session.LeaveOpen = session.SharesSocket))
                //{
                //    //if it doesn't then inform that close may occur?
                //    resp.AppendOrSetHeader(RtspHeaders.Connection, "close");
                //}

                ProcessSendRtspMessage(resp, session, sendResponse);

                session.ReleaseUnusedResources();

                resp = null;
                //Attempt to remove the sessionId when nothing is playing... why?
                //10.4 SETUP says we would have to bundle pipelined requests.
                //That is contradictory.

                //if (session.Playing.Count == 0) session.SessionId = null;

                string connectionHeader = request[RtspHeaders.Connection];

                if (false == string.IsNullOrWhiteSpace(connectionHeader))
                {
                    if (string.Compare(connectionHeader.Trim(), "close", true) == 0)
                    {
                        TryDisposeAndRemoveSession(session);
                    }
                }
            }
            else
            {
                //Create the response
                var resp = session.CreateRtspResponse(request);

                //Todo
                //Make a RtpInfo header for each stream ending...

                //Stop transport level activity if required
                if (session.m_RtpClient != null &&
                    session.m_RtpClient.IsActive) session.m_RtpClient.SendGoodbyes();

                //Remove all attachments and clear playing
                session.RemoveAllAttachmentsAndClearPlaying();

                //Send the response
                ProcessSendRtspMessage(resp, session, sendResponse);

                //Release any unused resources at this point
                session.ReleaseUnusedResources();

                //Remove the sessionId
                session.SessionId = null;

                resp = null;

                string connectionHeader = request[RtspHeaders.Connection];

                if (false == string.IsNullOrWhiteSpace(connectionHeader))
                {
                    if (string.Compare(connectionHeader.Trim(), "close", true) == 0)
                    {
                        TryDisposeAndRemoveSession(session);
                    }
                }
            }
        }
Example #33
0
        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <param name="sourceContext"></param>
        /// <returns></returns>
        /// //TODO Should be SourceMedia and SourceContext.
        internal RtspMessage ProcessSetup(RtspMessage request, RtpSource sourceStream, RtpClient.TransportContext sourceContext)
        {
            //Assign the sessionId now if it has not been assigned before.
            if (SessionId == null) SessionId = m_Id.GetHashCode().ToString();

            //We also have to send one back
            string returnTransportHeader = null;

            //Create a response
            RtspMessage response = CreateRtspResponse(request);

            Sdp.MediaDescription mediaDescription = sourceContext.MediaDescription;

            bool rtcpDisabled = sourceStream.m_DisableQOS;

            //Values in the header we need
            int clientRtpPort = -1, clientRtcpPort = -1, serverRtpPort = -1, serverRtcpPort = -1, localSsrc = 0, remoteSsrc = 0;

            //Cache this to prevent having to go to get it every time down the line
            IPAddress sourceIp = IPAddress.Any;

            string mode;

            bool unicast, multicast, interleaved, multiplexing;

            byte dataChannel = 0, controlChannel = 1;

            //Get the transport header
            string transportHeader = request[RtspHeaders.Transport];

            //If that is not present we cannot determine what transport the client wants
            if (string.IsNullOrWhiteSpace(transportHeader) ||
                false == (transportHeader.Contains("RTP")) ||
                false == RtspHeaders.TryParseTransportHeader(transportHeader,
                    out localSsrc, out sourceIp, out serverRtpPort, out serverRtcpPort, out clientRtpPort, out clientRtcpPort,
                    out interleaved, out dataChannel, out controlChannel, out mode, out unicast, out multicast))
            {
                return CreateRtspResponse(request, RtspStatusCode.BadRequest, "Invalid Transport Header");
            }

            //RTCP-mux: when RTSP 2.0 is official... (Along with Server Sent Messages)

            //Check if the ssrc was 0 which indicates any id
            if (localSsrc == 0) localSsrc = RFC3550.Random32((int)sourceContext.MediaDescription.MediaType);

            //Could also randomize the setupContext sequenceNumber here.
            //We need to make an TransportContext in response to a setup
            RtpClient.TransportContext setupContext = null;

             //Check for already setup stream and determine if the stream needs to be setup again or just updated
            if (Attached.ContainsKey(sourceContext))
            {
                //The contex may already existm should look first by ssrc.
                setupContext = m_RtpClient.GetContextForMediaDescription(sourceContext.MediaDescription);

                //If the context exists
                if (setupContext != null)
                {
                    //Update the ssrc  if it doesn't match.
                    if (localSsrc != 0 && setupContext.SynchronizationSourceIdentifier != localSsrc)
                    {
                        setupContext.SynchronizationSourceIdentifier = localSsrc;

                        if (remoteSsrc != 0 && setupContext.RemoteSynchronizationSourceIdentifier != remoteSsrc) setupContext.RemoteSynchronizationSourceIdentifier = remoteSsrc;
                    }

                    multicast = Media.Common.Extensions.IPAddress.IPAddressExtensions.IsMulticast(((IPEndPoint)setupContext.RemoteRtp).Address);

                    interleaved = setupContext.RtpSocket.ProtocolType == ProtocolType.Tcp && SharesSocket;

                    //Then indicate the information for that context in the return transport header.
                    returnTransportHeader = RtspHeaders.TransportHeader(setupContext.MediaDescription.MediaProtocol, setupContext.SynchronizationSourceIdentifier, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, ((IPEndPoint)setupContext.RemoteRtp).Port, ((IPEndPoint)setupContext.RemoteRtcp).Port, ((IPEndPoint)setupContext.LocalRtp).Port, ((IPEndPoint)setupContext.LocalRtcp).Port, false == multicast, multicast, null, interleaved, setupContext.DataChannel, setupContext.ControlChannel);

                    setupContext.LeaveOpen = interleaved;

                    //Attach logger (have option?)
                    m_RtpClient.Logger = m_Server.Logger;

                    goto UpdateContext;
                }
            }

            //Should determine intervals here for Rtcp from SessionDescription

            //Should determine if aggregate operation is allowed

            //Maybe setting up both udp and tcp at the same time? clientRtpPort needs to be nullable.
            //Maybe better to just give tokens from the function ..
            //Without explicitly checking for !interleaved VLC will recieve what it thinks are RTSP responses unless RTSP Interleaved is Forced.
            //Was trying to Quicktime to pickup RTSP Interleaved by default on the first response but it doesn't seem that easy (quick time tries to switch but fails?)

            //If the source does not force TCP and interleaved was not given and this is a unicast or multicast connection
            if (false == interleaved && (unicast || multicast))
            {

                //Check requested transport is allowed by server
                if (sourceStream.ForceTCP)//The client wanted Udp and Tcp was forced
                {
                    //Return the result
                    var result = CreateRtspResponse(request, RtspStatusCode.UnsupportedTransport);

                    //Indicate interleaved is required.
                    result.SetHeader(RtspHeaders.Transport, RtspHeaders.TransportHeader(RtpClient.RtpAvpProfileIdentifier + "/TCP", localSsrc, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, null, null, null, null, null, false, null, true, dataChannel, controlChannel));

                    return result;
                }

                //QuickTime debug

                if (clientRtpPort == 0) clientRtpPort = Media.Common.Extensions.Socket.SocketExtensions.FindOpenPort(ProtocolType.Udp, 30000, true);

                if (clientRtcpPort == 0) clientRtcpPort = clientRtpPort + 1;

                if (serverRtpPort == 0) serverRtpPort = Media.Common.Extensions.Socket.SocketExtensions.FindOpenPort(ProtocolType.Udp, 30000, true);

                if (serverRtcpPort == 0) serverRtcpPort = serverRtpPort + 1;

                //Ensure the ports are allowed to be used.
                if (m_Server.MaximumUdpPort.HasValue &&
                    (clientRtpPort > m_Server.MaximumUdpPort || clientRtcpPort > m_Server.MaximumUdpPort))
                {
                    //Handle port out of range
                    return CreateRtspResponse(request, RtspStatusCode.BadRequest, "Requested Udp Ports were out of range. Maximum Port = " + m_Server.MaximumUdpPort);
                }

                //Create sockets to reserve the ports.

                var localAddress = ((IPEndPoint)m_RtspSocket.LocalEndPoint).Address;

                Socket tempRtp = Media.Common.Extensions.Socket.SocketExtensions.ReservePort(SocketType.Dgram, ProtocolType.Udp, localAddress, clientRtpPort);

                Socket tempRtcp = Media.Common.Extensions.Socket.SocketExtensions.ReservePort(SocketType.Dgram, ProtocolType.Udp, localAddress, clientRtcpPort);

                //Check if the client was already created.
                if (m_RtpClient == null || m_RtpClient.IsDisposed)
                {
                    //Create a sender using a new segment on the existing buffer.
                    m_RtpClient = new RtpClient(new Common.MemorySegment(m_Buffer));

                    //Dont handle frame changed events from the client
                    m_RtpClient.FrameChangedEventsEnabled = false;

                    //Dont handle packets from the client
                    m_RtpClient.HandleIncomingRtpPackets = false;

                    //Attach the Interleaved data event
                    m_RtpClient.InterleavedData += m_RtpClient_InterleavedData;

                    //Attach logger (have option?)
                    m_RtpClient.Logger = m_Server.Logger;

                    //Use default data and control channel (Should be option?)
                    setupContext = new RtpClient.TransportContext(0, 1, localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);
                }
                else //The client was already created.
                {
                    //Have to calculate next data and control channel
                    RtpClient.TransportContext lastContext = m_RtpClient.GetTransportContexts().LastOrDefault();

                    if (lastContext != null) setupContext = new RtpClient.TransportContext((byte)(lastContext.DataChannel + 2), (byte)(lastContext.ControlChannel + 2), localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);
                    else setupContext = new RtpClient.TransportContext(dataChannel, controlChannel, localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);
                }

                //Initialize the Udp sockets
                setupContext.Initialize(localAddress, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, serverRtpPort, serverRtcpPort, clientRtpPort, clientRtcpPort);

                //Ensure the receive buffer size is updated for that context.
                Media.Common.ISocketReferenceExtensions.SetReceiveBufferSize(((Media.Common.ISocketReference)setupContext), m_Buffer.Count);

                ////Check if the punch packets made it out.
                //if ((setupContext.IsRtpEnabled && ((IPEndPoint)setupContext.RemoteRtp).Port == 0)
                //    ||
                //    (setupContext.IsRtcpEnabled && ((IPEndPoint)setupContext.RemoteRtcp).Port == 0))
                //{
                //    //Response should be a 461 or we should indicate the remote party is not yet listening in the response
                //    //Could also use StatusCode (100) with a reason phrase or header
                //}

                //Add the transportChannel
                m_RtpClient.TryAddContext(setupContext);

                //Create the returnTransportHeader (Should be setupContext.SynchronizationSourceIdentifier)
                returnTransportHeader = RtspHeaders.TransportHeader(RtpClient.RtpAvpProfileIdentifier, localSsrc, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, clientRtpPort, clientRtcpPort, serverRtpPort, serverRtcpPort, true, false, null, false, 0, 0);

                tempRtp.Dispose();

                tempRtp = null;

                tempRtcp.Dispose();

                tempRtcp = null;

            }

            //Check for 'interleaved' token or TCP being forced
            if (sourceStream.ForceTCP || interleaved)
            {
                //Check if the client was already created.
                if (m_RtpClient == null || m_RtpClient.IsDisposed)
                {
                    //Create a sender using a new segment on the existing buffer.
                    m_RtpClient = new RtpClient(new Common.MemorySegment(m_Buffer));

                    m_RtpClient.InterleavedData += m_RtpClient_InterleavedData;

                    //Attach logger (have option?)
                    m_RtpClient.Logger = m_Server.Logger;

                    #region Unused [Helps with debugging]

                    //m_RtpClient.RtcpPacketReceieved += m_RtpClient_RecievedRtcp;

                    //m_RtpClient.RtpPacketReceieved += m_RtpClient_RecievedRtp;

                    //m_RtpClient.RtcpPacketSent += m_RtpClient_SentRtcp;

                    #endregion

                    //Dont handle frame changed events from the client
                    m_RtpClient.FrameChangedEventsEnabled = false;

                    //Dont handle packets from the client
                    m_RtpClient.HandleIncomingRtpPackets = false;

                    //Create a new Interleave (don't use what was given as data or control channels)
                    setupContext = new RtpClient.TransportContext((byte)(dataChannel = 0), (byte)(controlChannel = 1), localSsrc, mediaDescription, m_RtspSocket, false == rtcpDisabled, remoteSsrc, 0);

                    //Add the transportChannel the client requested
                    m_RtpClient.TryAddContext(setupContext);

                    //Initialize the Interleaved Socket
                    setupContext.Initialize(m_RtspSocket, m_RtspSocket);
                }
                else //The client was already created
                {
                    //Have to calculate next data and control channel
                    RtpClient.TransportContext lastContext = m_RtpClient.GetTransportContexts().LastOrDefault();

                    //Don't use what was given as data or control channels
                    if (lastContext != null) setupContext = new RtpClient.TransportContext(dataChannel = (byte)(lastContext.DataChannel + 2), controlChannel = (byte)(lastContext.ControlChannel + 2), localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);
                    else setupContext = new RtpClient.TransportContext(dataChannel, controlChannel, localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);

                    //Add the transportChannel the client requested
                    m_RtpClient.TryAddContext(setupContext);

                    //Initialize the current TransportChannel with the interleaved Socket
                    setupContext.Initialize(m_RtspSocket, m_RtspSocket);
                }

                //Create the returnTransportHeader
                //returnTransportHeader = RtspHeaders.TransportHeader(RtpClient.RtpAvpProfileIdentifier + "/TCP", setupContext.SynchronizationSourceIdentifier, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, LocalEndPoint.Port, LocalEndPoint.Port, ((IPEndPoint)RemoteEndPoint).Port, ((IPEndPoint)RemoteEndPoint).Port, true, false, null, true, dataChannel, controlChannel);

                //Leave the socket open when disposing the RtpClient
                setupContext.LeaveOpen = true;

                returnTransportHeader = RtspHeaders.TransportHeader(RtpClient.RtpAvpProfileIdentifier + "/TCP", localSsrc, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, null, null, null, null, null, false, null, true, dataChannel, controlChannel);
            }

            //Add the new source
            Attached.Add(sourceContext, sourceStream);

            UpdateContext:

            //Synchronize the context sequence numbers
            setupContext.SequenceNumber = sourceContext.SequenceNumber;

            //Start and end times are always equal.
            setupContext.MediaStartTime = sourceContext.MediaStartTime;
            setupContext.MediaEndTime = sourceContext.MediaEndTime;

            //Set the returnTransportHeader to the value above
            response.SetHeader(RtspHeaders.Transport, returnTransportHeader);

            //Give the sessionid for the transport setup
            response.SetHeader(RtspHeaders.Session, SessionId);

            return response;
        }
Example #34
0
        internal RtspMessage ProcessRecord(RtspMessage request, IMedia source)
        {
            //Can't record when no Archiver is present
            if (m_Server.Archiver == null) return CreateRtspResponse(request, RtspStatusCode.PreconditionFailed, "No Server Archiver.");

            //If already archiving then indicate created
            if (m_Server.Archiver.IsArchiving(source)) return CreateRtspResponse(request, RtspStatusCode.Created);

            //Start archiving
            m_Server.Archiver.Start(source);

            //Return ok response
            return CreateRtspResponse(request);
        }
Example #35
0
        /// <summary>
        /// Sends a Rtsp Response on the given client session
        /// </summary>
        /// <param name="message">The RtspResponse to send</param> If this was byte[] then it could handle http
        /// <param name="ci">The session to send the response on</param>
        internal void ProcessSendRtspMessage(RtspMessage message, ClientSession session, bool sendResponse = true)
        {
            //Todo have a SupportedFeatures and RequiredFeatures HashSet.

            //Check Require Header
            //       And
            /* Add Unsupported Header if needed
            Require: play.basic, con.persistent
                       (basic play, TCP is supported)
            setup.playing means that setup and teardown can be used in the play state.
            */

            //If we have a session
            if (session == null || session.IsDisposed) return;

            try
            {
                //if there is a message to send
                if (message != null && false == message.IsDisposed)
                {
                    //AddServerHeaders()->

                    if (false == message.ContainsHeader(RtspHeaders.Server)) message.SetHeader(RtspHeaders.Server, ServerName);

                    if (false == message.ContainsHeader(RtspHeaders.Date)) message.SetHeader(RtspHeaders.Date, DateTime.UtcNow.ToString("r"));

                    #region RFC2326 12.38 Timestamp / Delay

                    /*
                         12.38 Timestamp

                           The timestamp general header describes when the client sent the
                           request to the server. The value of the timestamp is of significance
                           only to the client and may use any timescale. The server MUST echo
                           the exact same value and MAY, if it has accurate information about
                           this, add a floating point number indicating the number of seconds
                           that has elapsed since it has received the request. The timestamp is
                           used by the client to compute the round-trip time to the server so
                           that it can adjust the timeout value for retransmissions.

                           Timestamp  = "Timestamp" ":" *(DIGIT) [ "." *(DIGIT) ] [ delay ]
                           delay      =  *(DIGIT) [ "." *(DIGIT) ]
                         */

                    if (false == message.ContainsHeader(RtspHeaders.Timestamp)
                        &&
                        session.LastRequest != null
                        &&
                        session.LastRequest.ContainsHeader(RtspHeaders.Timestamp))
                    {
                        ////Apparently not joined with ;
                        ////message.SetHeader(RtspHeaders.Timestamp, session.LastRequest[RtspHeaders.Timestamp] + "delay=" + (DateTime.UtcNow - session.LastRequest.Created).TotalSeconds);

                        //Set the value of the Timestamp header as given
                        message.AppendOrSetHeader(RtspHeaders.Timestamp, session.LastRequest[RtspHeaders.Timestamp]);

                        //Add a delay datum
                        message.AppendOrSetHeader(RtspHeaders.Timestamp, "delay=" + (DateTime.UtcNow - session.LastRequest.Created).TotalSeconds);
                    }

                    #endregion

                    string sess = message.GetHeader(RtspHeaders.Session);

                    //Check for a session header
                    if (false == string.IsNullOrWhiteSpace(sess))
                    {
                        //Add the timeout header if there was a session header.
                        if (RtspClientInactivityTimeout > TimeSpan.Zero && false == sess.Contains("timeout")) message.AppendOrSetHeader(RtspHeaders.Session, "timeout=" + (int)(RtspClientInactivityTimeout.TotalSeconds / 2));
                    }

                    //Oops
                    //if (session.m_RtspSocket.ProtocolType == ProtocolType.Tcp && session.Attached.Count > 0) response.SetHeader("Ignore", "$0\09\r\n$\0:\0");

                    //Dispose the last response
                    if (session.LastResponse != null) session.LastResponse.Dispose();

                    //Todo
                    //Content-Encoding should be the same as the request's if possible..

                    //If sending a response
                    if (sendResponse)
                    {
                        //Log response
                        if (Logger != null) Logger.LogResponse(message, session);

                        session.SendRtspData((session.LastResponse = message).ToBytes());

                    }//Otherwise just update the property
                    else session.LastResponse = message;

                    //Indicate the session is not disconnected
                    session.IsDisconnected = false;

                    //Indicate the last response was sent now.
                    session.LastResponse.Transferred = DateTime.UtcNow;
                }
                else
                {
                    //Test the connectivity and start another receive
                    session.SendRtspData(Media.Common.MemorySegment.EmptyBytes);
                }
            }
            catch (Exception ex)
            {
                if (Logger != null) Logger.LogException(ex);

                //if a socket exception occured then handle it.
                if (session != null && ex is SocketException) HandleClientSocketException((SocketException)ex, session);
            }
        }
Example #36
0
 /// <summary>
 /// Handles the SET_PARAMETER RtspRequest
 /// </summary>
 /// <param name="request">The GET_PARAMETER RtspRequest to handle</param>
 /// <param name="ci">The RtspSession from which the request was receieved</param>
 internal void ProcessSetParameter(RtspMessage request, ClientSession session, bool sendResponse = true)
 {
     //Could be used for PTZ or other stuff
     //Should have a way to determine to forward send parameters... public bool ForwardSetParameter { get; set; }
     //Should have a way to call SendSetParamter on the source if required.
     //Should allow sever parameters to be set?
     using (var resp = session.CreateRtspResponse(request))
     {
         //Content type
         ProcessSendRtspMessage(resp, session, sendResponse);
     }
 }
Example #37
0
        /// <summary>
        /// Authenticates a RtspRequest against a RtspStream
        /// </summary>
        /// <param name="request">The RtspRequest to authenticate</param>
        /// <param name="source">The RtspStream to authenticate against</param>
        /// <returns>True if authroized, otherwise false</returns>
        public virtual bool AuthenticateRequest(RtspMessage request, IMedia source)
        {
            if (request == null) throw new ArgumentNullException("request");
            if (source == null) throw new ArgumentNullException("source");

            //There are no rules to validate
            if (RequiredCredentials == null) return true;

            string authHeader = request.GetHeader(RtspHeaders.Authorization);

            string[] parts = null;

            string authType = null;

            NetworkCredential requiredCredential = null;

            if (string.IsNullOrWhiteSpace(authHeader))
            {
                authType = "basic";

                requiredCredential = RequiredCredentials.GetCredential(source.ServerLocation, authType);

                if (requiredCredential == null) requiredCredential = RequiredCredentials.GetCredential(source.ServerLocation, authType = "digest");
            }
            else
            {
                parts = authHeader.Split((char)Common.ASCII.Space);
                if (parts.Length > 0)
                {
                    authType = parts[0];
                    authHeader = parts[1];
                }
                requiredCredential = RequiredCredentials.GetCredential(source.ServerLocation, authType);
            }

            //If there is no rule to validate
            if (requiredCredential == null) return true;
            else
            {
                //Verify against credential

                //If the request does not have the authorization header then there is nothing else to determine
                if (string.IsNullOrWhiteSpace(authHeader)) return false;

                //Wouldn't have to have a RemoteAuthenticationScheme if we stored the Nonce and CNonce on the session... then allowed either or here based on the header

                //If the SourceAuthenticationScheme is Basic and the header contains the BASIC indication then validiate using BASIC authentication
                if (string.Compare(authType, "basic", true) == 0)
                {
                    //realm may be present?
                    //Basic realm="''" dasfhadfhsaghf

                    //Get the decoded value
                    authHeader = request.ContentEncoding.GetString(Convert.FromBase64String(authHeader));

                    //Get the parts
                    parts = authHeader.Split(':');

                    //If enough return the determination by comparison as the result
                    return parts.Length > 1 && (parts[0].Equals(requiredCredential.UserName) && parts[1].Equals(requiredCredential.Password));
                }
                else if (string.Compare(authType, "digest", true) == 0)
                {
                    //http://tools.ietf.org/html/rfc2617
                    //Digest RFC2617
                    /* Example header -
                     *
                     Authorization: Digest username="******",
                         realm="*****@*****.**",
                         nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                         uri="/dir/index.html",
                         qop=auth,
                         nc=00000001,
                         cnonce="0a4f113b",
                         response="6629fae49393a05397450978507c4ef1",
                         opaque="5ccc069c403ebaf9f0171e9517f40e41"
                     *
                     *
                     * Example Convo
                     *
                     * ANNOUNCE rtsp://216.224.181.197/bstream.sdp RTSP/1.0
            CSeq: 1
            Content-Type: application/sdp
            User-Agent: C.U.
            Authorization: Digest username="******", realm="null", nonce="null", uri="/bstream.sdp", response="239fcac559661c17436e427e75f3d6a0"
            Content-Length: 313

            v=0
            s=CameraStream
            m=video 5006 RTP/AVP 96
            b=RR:0
            a=rtpmap:96 H264/90000
            a=fmtp:96 packetization-mode=1;profile-level-id=42000c;sprop-parameter-sets=Z0IADJZUCg+I,aM44gA==;
            a=control:trackID=0
            m=audio 5004 RTP/AVP 96
            b=AS:128
            b=RR:0
            a=rtpmap:96 AMR/8000
            a=fmtp:96 octet-align=1;
            a=control:trackID=1

            RTSP/1.0 401 Unauthorized
            Server: DSS/6.0.3 (Build/526.3; Platform/Linux; Release/Darwin Streaming Server; State/Development; )
            Cseq: 1
            WWW-Authenticate: Digest realm="Streaming Server", nonce="e5c0b7aff71820962027d73f55fe48c8"

            ANNOUNCE rtsp://216.224.181.197/bstream.sdp RTSP/1.0
            CSeq: 2
            Content-Type: application/sdp
            User-Agent: C.U.
            Authorization: Digest username="******", realm="Streaming Server", nonce="e5c0b7aff71820962027d73f55fe48c8", uri="/bstream.sdp", response="6e3aa3be3f5c04a324491fe9ab341918"
            Content-Length: 313

            v=0
            s=CameraStream
            m=video 5006 RTP/AVP 96
            b=RR:0
            a=rtpmap:96 H264/90000
            a=fmtp:96 packetization-mode=1;profile-level-id=42000c;sprop-parameter-sets=Z0IADJZUCg+I,aM44gA==;
            a=control:trackID=0
            m=audio 5004 RTP/AVP 96
            b=AS:128
            b=RR:0
            a=rtpmap:96 AMR/8000
            a=fmtp:96 octet-align=1;
            a=control:trackID=1

            RTSP/1.0 200 OK
            Server: DSS/6.0.3 (Build/526.3; Platform/Linux; Release/Darwin Streaming Server; State/Development; )
            Cseq: 2
                     *
                     *
                     */

                    parts = authHeader.Split((char)Common.ASCII.Comma);

                    string username, realm, nonce, nc, cnonce, uri, qop, opaque, response;

                    username = parts.Where(p => p.StartsWith("username")).FirstOrDefault();

                    realm = parts.Where(p => p.StartsWith("realm")).FirstOrDefault();

                    nc = parts.Where(p => p.StartsWith("nc")).FirstOrDefault();

                    nonce = parts.Where(p => p.StartsWith("nonce")).FirstOrDefault();

                    if (nonce == null) nonce = string.Empty;

                    cnonce = parts.Where(p => p.StartsWith("cnonce")).FirstOrDefault();

                    if (cnonce == null) cnonce = string.Empty;

                    uri = parts.Where(p => p.StartsWith("uri")).FirstOrDefault();

                    qop = parts.Where(p => p.StartsWith("qop")).FirstOrDefault();

                    if (qop == null) qop = string.Empty;

                    opaque = parts.Where(p => p.StartsWith("opaque")).FirstOrDefault();

                    if (opaque == null) opaque = string.Empty;

                    response = parts.Where(p => p.StartsWith("response")).FirstOrDefault();

                    if (string.IsNullOrEmpty(username) || username != requiredCredential.UserName || string.IsNullOrWhiteSpace(realm) || string.IsNullOrWhiteSpace(uri) || string.IsNullOrWhiteSpace(response)) return false;

                    using (var md5 = Utility.CreateMD5HashAlgorithm())
                    {
                        //http://en.wikipedia.org/wiki/Digest_access_authentication
                        //The MD5 hash of the combined username, authentication realm and password is calculated. The result is referred to as HA1.
                        byte[] HA1 = md5.ComputeHash(request.ContentEncoding.GetBytes(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:{1}:{2}", requiredCredential.UserName, realm.Replace("realm=", string.Empty), requiredCredential.Password)));

                        //The MD5 hash of the combined method and digest URI is calculated, e.g. of "GET" and "/dir/index.html". The result is referred to as HA2.
                        byte[] HA2 = md5.ComputeHash(request.ContentEncoding.GetBytes(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:{1}", request.Method, uri.Replace("uri=", string.Empty))));

                        //No QOP No NC
                        //See http://en.wikipedia.org/wiki/Digest_access_authentication
                        //http://tools.ietf.org/html/rfc2617

                        //The MD5 hash of the combined HA1 result, server nonce (nonce), request counter (nc), client nonce (cnonce), quality of protection code (qop) and HA2 result is calculated. The result is the "response" value provided by the client.
                        byte[] ResponseHash = md5.ComputeHash(request.ContentEncoding.GetBytes(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:{1}:{2}:{3}:{4}:{5}", Convert.ToString(HA1).Replace("-", string.Empty), nonce.Replace("nonce=", string.Empty), nc.Replace("nc=", string.Empty), cnonce.Replace("cnonce=", string.Empty), qop.Replace("qop=", string.Empty), Convert.ToString(HA2).Replace("-", string.Empty))));

                        //return the result of a mutal hash creation via comparison
                        return ResponseHash.SequenceEqual(Media.Common.Extensions.String.StringExtensions.HexStringToBytes(response.Replace("response=", string.Empty)));
                    }
                }
                //else if (source.RemoteAuthenticationScheme == AuthenticationSchemes.IntegratedWindowsAuthentication && (header.Contains("ntlm") || header.Contains("integrated")))
                //{
                //    //Check windows creds
                //    throw new NotImplementedException();
                //}

            }

            //Did not authenticate
            return false;
        }
 internal override void LogResponse(RtspMessage response, ClientSession session)
 {
     try { Logger.Log(string.Format(Format, response.MessageType, response.CSeq, response.StatusCode, session.Id, null)); }
     catch { throw; }
 }
 internal override void LogRequest(RtspMessage request, ClientSession session)
 {
     try { Logger.Log(string.Format(Format, request.MessageType, request.Method, request.Location, session.Id, null)); }
     catch { throw; }
 }
Example #40
0
        /// <summary>
        /// Sends the Rtcp Goodbye and detaches all sources
        /// </summary>
        public override void Dispose()
        {
            if (IsDisposed) return;

            base.Dispose();

            RemoveAllAttachmentsAndClearPlaying();

            //Mark as disconnected
            IsDisconnected = true;

            //Disconnect the RtpClient so it's not hanging around wasting resources for nothing
            if (m_RtpClient != null)
            {
                try
                {
                    m_RtpClient.InterleavedData -= m_RtpClient_InterleavedData;

                    m_RtpClient.Dispose();

                    m_RtpClient = null;
                }
                catch { }
            }

            if (m_Buffer != null)
            {
                try
                {
                    m_Buffer.Dispose();

                    m_Buffer = null;
                }
                catch { }
            }

            if (m_RtspSocket != null)
            {
                try
                {
                    if (false == LeaveOpen) m_RtspSocket.Dispose();

                    m_RtspSocket = null;
                }
                catch { }
            }

            if (LastRequest != null)
            {
                try
                {
                    LastRequest.Dispose();

                    LastRequest = null;
                }
                catch { }
            }

            if (LastResponse != null)
            {
                try
                {
                    LastResponse.Dispose();

                    LastResponse = null;
                }
                catch { }
            }

            m_Server = m_Contained = null;
        }
Example #41
0
        private void ProcessRtspRecord(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            var found = FindStreamByLocation(request.Location);

            if (found == null)
            {
                ProcessLocationNotFoundRtspRequest(session, sendResponse);
                return;
            }

            if (false == AuthenticateRequest(request, found))
            {
                ProcessAuthorizationRequired(found, session, sendResponse);
                return;
            }

            if (false == found.Ready || Archiver == null)
            {
                ProcessInvalidRtspRequest(session, RtspStatusCode.PreconditionFailed, null, sendResponse);
                return;
            }

            using (var resp = session.ProcessRecord(request, found))
            {
                ProcessSendRtspMessage(resp, session, sendResponse);
            }
        }
Example #42
0
        /// <summary>
        /// Creates a RtspResponse based on the SequenceNumber contained in the given RtspRequest
        /// </summary>
        /// <param name="request">The request to utilize the SequenceNumber from, if null the current SequenceNumber is used</param>
        /// <param name="statusCode">The StatusCode of the generated response</param>
        /// <returns>The RtspResponse created</returns>
        internal RtspMessage CreateRtspResponse(RtspMessage request = null, RtspStatusCode statusCode = RtspStatusCode.OK, string body = null)
        {
            RtspMessage response = new RtspMessage(RtspMessageType.Response);

            response.StatusCode = statusCode;

            /*
             12.4.1 400 Bad Request

               The request could not be understood by the server due to malformed
               syntax. The client SHOULD NOT repeat the request without
               modifications [H10.4.1]. If the request does not have a CSeq header,
               the server MUST NOT include a CSeq in the response.
             */

            if (request != null)
            {
                if (request.ContainsHeader(RtspHeaders.Session)) response.SetHeader(RtspHeaders.Session, request.GetHeader(RtspHeaders.Session));

                if (statusCode != RtspStatusCode.BadRequest) response.CSeq = request.CSeq;
            }
            else if (statusCode != RtspStatusCode.BadRequest && LastRequest.CSeq >= 0) response.CSeq = LastRequest.CSeq;

            //Include any body.
            if (false == string.IsNullOrWhiteSpace(body)) response.Body = body;

            return response;
        }
Example #43
0
 internal void ProcessInvalidRtspRequest(RtspMessage response, ClientSession session, bool sendResponse = true)
 {
     ProcessSendRtspMessage(response, session, sendResponse);
 }
Example #44
0
        /// <summary>
        /// Process a Rtsp DESCRIBE.
        /// Re-writes the Sdp.SessionDescription in a manner which contains the values of the server and not of the origional source.
        /// </summary>
        /// <param name="describeRequest">The request received from the server</param>
        /// <param name="source">Tje source stream to describe</param>
        /// <returns>A RtspMessage with a Sdp.SessionDescription in the Body and ContentType set to application/sdp</returns>
        internal RtspMessage ProcessDescribe(RtspMessage describeRequest, SourceMedia source)
        {
            RtspMessage describeResponse = CreateRtspResponse(describeRequest);

            describeResponse.SetHeader(RtspHeaders.ContentType, Sdp.SessionDescription.MimeType);

            //Don't cache this SDP
            describeResponse.SetHeader(RtspHeaders.CacheControl, "no-cache");
            //describeResponse.SetHeader(RtspHeaders.Pragma, "no-cache");

            //If desired you will need a way to determine when the last time the sdp was modified, could use the sessionId from the SDP or its' created property etc.)
            //describeResponse.SetHeader(RtspHeaders.LastModified,

            if (describeRequest.Location.ToString().ToLowerInvariant().Contains("live"))
            {
                describeResponse.SetHeader(RtspHeaders.ContentBase, "rtsp://" + ((IPEndPoint)m_RtspSocket.LocalEndPoint).Address.ToString() + "/live/" + source.Id + '/');
            }
            else
            {
                describeResponse.SetHeader(RtspHeaders.ContentBase, describeRequest.Location.ToString());
            }

            //Create a Session Description to describe the media requested
            using (var sessionDescription = CreateSessionDescription(source))
            {
                //Set the body
                describeResponse.Body = sessionDescription.ToString();

                //Clients sessionId is created from the Sdp's SessionId Line
                //if (string.IsNullOrWhiteSpace(SessionId)) SessionId = sessionDescription.SessionId;
            }

            //Return the resulting message
            return describeResponse;
        }
Example #45
0
        /// <summary>
        /// Handles the GET_PARAMETER RtspRequest
        /// </summary>
        /// <param name="request">The GET_PARAMETER RtspRequest to handle</param>
        /// <param name="ci">The RtspSession from which the request was receieved</param>
        internal void ProcessGetParameter(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            //TODO Determine API
            //packets_sent
            //jitter
            //rtcp_interval

            var resp = session.CreateRtspResponse(request);

            resp.SetHeader(RtspHeaders.Connection, "Keep-Alive");

            ProcessSendRtspMessage(resp, session, sendResponse);

            resp = null;
        }
Example #46
0
        internal RtspMessage ProcessPause(RtspMessage request, RtpSource source)
        {
            //If the source is attached
            if (Attached.ContainsValue(source))
            {
                //Iterate the source transport contexts
                foreach (RtpClient.TransportContext sourceContext in source.RtpClient.GetTransportContexts())
                {
                    //Adding the id will stop the packets from being enqueued into the RtpClient
                    PacketBuffer.Add((int)sourceContext.SynchronizationSourceIdentifier);
                }

                //Return the response
                return CreateRtspResponse(request);
            }

            //The source is not attached
            return CreateRtspResponse(request, RtspStatusCode.MethodNotValidInThisState);
        }
        /// <summary>
        /// Configures the transport and forwarder.
        /// </summary>
        /// <param name="aMessage">A message.</param>
        /// <param name="forwarder">The preset forwarder.</param>
        /// <returns>The configured forwarder.</returns>
        private static Forwarder ConfigureTransportAndForwarder(RtspMessage aMessage, UDPForwarder forwarder)
        {
            RtspTransport transport = RtspTransport.Parse(aMessage.Headers[RtspHeaderNames.Transport]);

            Forwarder resultForwarder;

            if (transport.LowerTransport == RtspTransport.LowerTransportType.UDP)
            {
                if (transport.ServerPort != null)
                {
                    forwarder.SourcePortVideo    = transport.ServerPort.First;
                    forwarder.ForwardPortCommand = transport.ServerPort.Second;
                }
                resultForwarder = forwarder;
            }
            else
            {
                TCPtoUDPForwader TCPForwarder = new TCPtoUDPForwader();
                TCPForwarder.ForwardCommand            = aMessage.SourcePort;
                TCPForwarder.SourceInterleavedVideo    = transport.Interleaved.First;
                TCPForwarder.ForwardInterleavedCommand = transport.Interleaved.Second;
                // we need to transfer already getted values
                TCPForwarder.ForwardHostVideo  = forwarder.ForwardHostVideo;
                TCPForwarder.ForwardPortVideo  = forwarder.ForwardPortVideo;
                TCPForwarder.SourcePortCommand = forwarder.SourcePortCommand;
                TCPForwarder.ToMulticast       = forwarder.ToMulticast;

                resultForwarder = TCPForwarder;
            }

            if (resultForwarder.ToMulticast)
            {
                // Setup port and destination multicast.
                resultForwarder.ForwardHostVideo = CreateNextMulticastAddress();
                resultForwarder.ForwardPortVideo = forwarder.FromForwardVideoPort;

                RtspTransport newTransport = new RtspTransport()
                {
                    IsMulticast = true,
                    Destination = resultForwarder.ForwardHostVideo,
                    Port        = new PortCouple(resultForwarder.ForwardPortVideo, resultForwarder.ListenCommandPort)
                };
                if ((resultForwarder is UDPForwarder && forwarder.ForwardPortCommand == 0) ||
                    (resultForwarder is TCPtoUDPForwader && (resultForwarder as TCPtoUDPForwader).ForwardInterleavedCommand == 0))
                {
                    newTransport.Port = null;
                }
                aMessage.Headers[RtspHeaderNames.Transport] = newTransport.ToString();
            }
            else
            {
                RtspTransport newTransport = new RtspTransport()
                {
                    IsMulticast = false,
                    Destination = forwarder.ForwardHostVideo,
                    ClientPort  = new PortCouple(resultForwarder.ForwardPortVideo, resultForwarder.SourcePortCommand),
                    ServerPort  = new PortCouple(resultForwarder.FromForwardVideoPort, resultForwarder.ListenCommandPort)
                };
                if ((resultForwarder is UDPForwarder && forwarder.ForwardPortCommand == 0) ||
                    (resultForwarder is TCPtoUDPForwader && (resultForwarder as TCPtoUDPForwader).ForwardInterleavedCommand == 0))
                {
                    newTransport.ServerPort = null;
                }
                aMessage.Headers[RtspHeaderNames.Transport] = newTransport.ToString();
            }

            return(resultForwarder);
        }
Example #48
0
        internal RtspMessage ProcessPlay(RtspMessage playRequest, RtpSource source)
        {
            //Determine if the request can be processed.
            bool playAllowed = false;

            //Indicate why play would not be allowed.
            string information = string.Empty;

            //Check the there is an underlying transport which has not already been disposed.
            if (m_RtpClient == null
                ||
                true == m_RtpClient.IsDisposed)
            {
                //Indicate the SETUP needs to occur again.
                information = "Session Transport closed. Perform SETUP.";

                //REDIRECT TO SETUP?

            }
            else playAllowed = true;

            //Get the contexts which are available if play is allowed.
            IEnumerable<RtpClient.TransportContext> sourceAvailable = Enumerable.Empty<RtpClient.TransportContext>();

            //If there is a tranport which can communicate the media then determine if there is an applicable source.
            if (playAllowed)
            {
                //Query the source's transport for a context which has been attached to the session via SETUP.
                sourceAvailable = source.RtpClient.GetTransportContexts().Where(sc => GetSourceContext(sc.MediaDescription) != null);

                //If any context is available then this PLAY request can be honored.
                playAllowed = sourceAvailable.Any();

                //Information
                information = "No Source Transport. Perform SETUP.";

                //REDIRECT TO SETUP?
            }

            //13.4.16 464 Data Transport Not Ready Yet
            //The data transmission channel to the media destination is not yet ready for carrying data.
            if (false == playAllowed)
            {
                return CreateRtspResponse(playRequest, RtspStatusCode.DataTransportNotReadyYet, information);
            }

            //bool allowIncomingRtp = false;

            //Attach the packet events if not already attached (E.g. paused)
            if (false == Playing.Contains(source.Id))
            {
                //Attach events based on how the source will raise them.
                if (source.RtpClient.FrameChangedEventsEnabled) source.RtpClient.RtpFrameChanged += OnSourceFrameChanged;
                else source.RtpClient.RtpPacketReceieved += OnSourceRtpPacketRecieved;

                //Ensure playing
                Playing.Add(source.Id);
            }

            m_RtpClient.FrameChangedEventsEnabled = false;

            //m_RtpClient.IncomingPacketEventsEnabled = allowIncomingRtp;

            //Prepare a place to hold the response
            RtspMessage playResponse = CreateRtspResponse(playRequest);

            //Get the Range header
            string rangeHeader = playRequest[RtspHeaders.Range];

            TimeSpan? startRange = null, endRange = null;

            /*
             A PLAY request without a Range header is legal. It starts playing a
             stream from the beginning unless the stream has been paused. If a
             stream has been paused via PAUSE, stream delivery resumes at the
             pause point. If a stream is playing, such a PLAY request causes no
             further action and can be used by the client to test server liveness.
               */

            //Determine if the client wants to start playing from a specific point in time or until a specific point
            if (false == string.IsNullOrWhiteSpace(rangeHeader))
            {

                //TODO
                //If the source does not support seeking then a 456 must be returned.
                //Will require a property or convention in the SessionDescripton e.g. a=broadcast.
                //return CreateRtspResponse(playRequest, RtspStatusCode.HeaderFieldNotValidForResource);

                string type; TimeSpan start, end;

                //If parsing of the range header was successful
                if (RtspHeaders.TryParseRange(rangeHeader, out type, out start, out end))
                {
                    //Determine the max start time
                    TimeSpan max = sourceAvailable.Max(tc => tc.MediaEndTime);

                    //Start playing from here
                    startRange = start;

                    //End playing after this time if given and not unspecified
                    endRange = end;

                    //http://stackoverflow.com/questions/4672359/why-does-timespan-fromsecondsdouble-round-to-milliseconds

                    if(end != Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan
                        &&
                        (end += Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan) > max) return CreateRtspResponse(playRequest, RtspStatusCode.InvalidRange, "Invalid End Range");

                    //If the given time to start at is > zero
                    if (start > TimeSpan.Zero)
                    {
                        //If the maximum is not infinite and the start exceeds the max indicate this.
                        if (max != Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan
                            &&
                            start > max) return CreateRtspResponse(playRequest, RtspStatusCode.InvalidRange, "Invalid Start Range");
                    }

                    //If the end time is infinite and the max is not infinite then the end is the max time.
                    if (end == Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan && max != Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan) endRange = end = max;

                    //If the start time is 0 and the end time is not infinite then start the start time to the uptime of the stream (how long it has been playing)
                    if (start == TimeSpan.Zero && end != Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan) startRange = start = source.RtpClient.Uptime;
                    else startRange = null;
                }
            }

            //Todo Process Scale, Speed, Bandwidth, Blocksize

            //Set Seek-Style to indicate if Seeking is supported.

            //Prepare the RtpInfo header
            //Iterate the source's TransportContext's to Augment the RtpInfo header for the current request
            List<string> rtpInfos = new List<string>();

            string lastSegment = playRequest.Location.Segments.Last();

            Sdp.MediaType mediaType;

            //If the mediaType was specified
            if (Enum.TryParse(lastSegment, out mediaType))
            {
                var sourceContext = sourceAvailable.FirstOrDefault(tc => tc.MediaDescription.MediaType == mediaType);

                //AggreateOperationNotAllowed?
                if (sourceContext == null) return CreateRtspResponse(playRequest, RtspStatusCode.BadRequest, "Source Not Setup");

                var context = m_RtpClient.GetContextForMediaDescription(sourceContext.MediaDescription);

                //Create the RtpInfo header for this context.

                //There should be a better way to get the Uri for the stream
                //E.g. ServerLocation should be used.

                //UriEnecode?

                bool hasAnyState = sourceContext.RtpPacketsReceived > 0 || sourceContext.RtpPacketsSent > 0 && false == context.InDiscovery;

                rtpInfos.Add(RtspHeaders.RtpInfoHeader(new Uri("rtsp://" + ((IPEndPoint)(m_RtspSocket.LocalEndPoint)).Address + "/live/" + source.Id + '/' + context.MediaDescription.MediaType.ToString()),
                    hasAnyState ? sourceContext.SequenceNumber : (int?)null,
                    hasAnyState ? sourceContext.RtpTimestamp : (int?)null,
                    hasAnyState ? (int?)null : context.SynchronizationSourceIdentifier));

                //Identify now to emulate GStreamer :P
                m_RtpClient.SendSendersReport(context);

                //Done with context.
                context = null;
            }
            else
            {
                foreach (RtpClient.TransportContext sourceContext in sourceAvailable)
                {
                    var context = m_RtpClient.GetContextForMediaDescription(sourceContext.MediaDescription);

                    if (context == null) continue;

                    //Create the RtpInfo header for this context.

                    //UriEnecode?

                    //There should be a better way to get the Uri for the stream
                    //E.g. ServerLocation should be used.
                    bool hasAnyState = sourceContext.RtpPacketsReceived > 0 || sourceContext.RtpPacketsSent > 0 && false == context.InDiscovery;

                    rtpInfos.Add(RtspHeaders.RtpInfoHeader(new Uri("rtsp://" + ((IPEndPoint)(m_RtspSocket.LocalEndPoint)).Address + "/live/" + source.Id + '/' + context.MediaDescription.MediaType.ToString()),
                        hasAnyState ? sourceContext.SequenceNumber : (int?)null,
                        hasAnyState ? sourceContext.RtpTimestamp : (int?)null,
                        hasAnyState ? (int?)null : context.SynchronizationSourceIdentifier));

                    //Done with context.
                    context = null;
                }

                //Send all reports
                m_RtpClient.SendSendersReports();
            }

            //Indicate the range of the play response. (`Range` will be 'now-' if no start or end was given)
            playResponse.SetHeader(RtspHeaders.Range, RtspHeaders.RangeHeader(startRange, endRange));

            //Sent the rtpInfo
            playResponse.AppendOrSetHeader(RtspHeaders.RtpInfo, string.Join(", ", rtpInfos.ToArray()));

            //Todo
            //Set the MediaProperties header.

            //Ensure RtpClient is now connected connected so packets will begin to go out when enqued
            if (false == m_RtpClient.IsActive)
            {
                m_RtpClient.Activate();

                //m_RtpClient.m_WorkerThread.Priority = ThreadPriority.Highest;
            }

            //Return the response
            return playResponse;
        }
Example #49
0
        internal void ProcessClientBuffer(ClientSession session, int received)
        {
            if (session == null || session.IsDisposed || received <= 0) return;

            try
            {
                //Use a segment around the data received which is already in the buffer.
                using (Common.MemorySegment data = new Common.MemorySegment(session.m_Buffer.Array, session.m_Buffer.Offset, received))
                {

                    if (data[0] == RtpClient.BigEndianFrameControl)
                    {
                        if (session.m_RtpClient == null) return;

                        received -= session.m_RtpClient.ProcessFrameData(session.m_Buffer.Array, session.m_Buffer.Offset, received, session.m_RtspSocket);
                    }

                    if (received <= 0) return;

                    //Ensure the message is really Rtsp
                    RtspMessage request = new RtspMessage(data);

                    //Check for validity
                    if (request.MessageType != RtspMessageType.Invalid)
                    {
                        //Process the request when complete
                        if (request.IsComplete)
                        {
                            ProcessRtspRequest(request, session);

                            return;
                        }

                    } //Otherwise if LastRequest is not disposed then attempt completion with the invalid data
                    else if (session.LastRequest != null && false == session.LastRequest.IsDisposed)
                    {
                        //Indicate a CompleteFrom is occuring
                        Media.Common.ILoggingExtensions.Log(Logger, "Session:" + session.Id + " Attempting to complete previous mesage with buffer of " + data.Count + " bytes.");

                        //Attempt to complete it
                        received = session.LastRequest.CompleteFrom(null, data);

                        //If nothing was recieved then do nothing.
                        if (received == 0)
                        {
                            Media.Common.ILoggingExtensions.Log(Logger, "Session:" + session.Id + "Did not use buffer of " + data.Count + " bytes.");

                            //return;
                        }

                        Media.Common.ILoggingExtensions.Log(Logger, "Session:" + session.Id + " used " + received + " of buffer bytes");

                        //Account for the received data
                        session.m_Receieved += received;

                        //Determine how to process the messge

                        switch (session.LastRequest.MessageType)
                        {
                            case RtspMessageType.Response:
                            case RtspMessageType.Invalid:
                                {
                                    //Ensure the session is still connected.
                                    session.SendRtspData(Media.Common.MemorySegment.EmptyBytes);

                                    return;
                                }
                            case RtspMessageType.Request:
                                {
                                    //If the request is complete now then process it
                                    if (session.LastRequest.IsComplete)
                                    {
                                        //Process the request (it may not be complete yet)
                                        ProcessRtspRequest(session.LastRequest, session);

                                        return;
                                    }

                                    goto case RtspMessageType.Invalid;
                                }
                        }
                    }

                    //Log the invalid request
                    Media.Common.ILoggingExtensions.Log(Logger, "Received Invalid Message:" + request + " \r\nFor Session:" + session.Id);

                    if (session.LastRequest != null) session.LastRequest.Dispose();

                    //Store it for now to allow completion.
                    session.LastRequest = request;

                    //Ensure the session is still connected.
                    session.SendRtspData(Media.Common.MemorySegment.EmptyBytes);
                }
            }
            catch(Exception ex)
            {
                if (Logger != null) Logger.LogException(ex);
            }
        }
Example #50
0
        /// <summary>
        /// Sets the given session up, TODO Make functions which help with creation of TransportContext and Initialization
        /// </summary>
        /// <param name="request"></param>
        /// <param name="session"></param>
        internal void ProcessRtspSetup(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            RtpSource found = FindStreamByLocation(request.Location) as RtpSource;

            if (found == null)
            {
                //This allows probing for streams even if not authenticated....
                ProcessLocationNotFoundRtspRequest(session, sendResponse);
                return;
            }
            else if (false == AuthenticateRequest(request, found))
            {
                ProcessAuthorizationRequired(found, session, sendResponse);
                return;
            }
            else if (false == found.Ready)
            {
                ProcessInvalidRtspRequest(session, RtspStatusCode.PreconditionFailed, null, sendResponse);
                return;
            }

            //The source is ready

            //Determine if we have the track
            string track = request.Location.Segments.Last().Replace("/", string.Empty);

            Sdp.MediaDescription mediaDescription = found.SessionDescription.MediaDescriptions.FirstOrDefault(md => string.Compare(track, md.MediaType.ToString(), true, System.Globalization.CultureInfo.InvariantCulture) == 0);

            ////Find the MediaDescription for the request based on the track variable
            //foreach (Sdp.MediaDescription md in found.SessionDescription.MediaDescriptions)
            //{
            //    Sdp.SessionDescriptionLine attributeLine = md.Lines.Where(l => l.Type == 'a' && l.Parts.Any(p => p.Contains("control"))).FirstOrDefault();
            //    if (attributeLine != null)
            //    {
            //        string actualTrack = attributeLine.Parts.Where(p => p.Contains("control")).First().Replace("control:", string.Empty);
            //        if(actualTrack == track || actualTrack.Contains(track))
            //        {
            //            mediaDescription = md;
            //            break;
            //        }
            //    }
            //}

            //Cannot setup media
            if (mediaDescription == null)
            {
                ProcessLocationNotFoundRtspRequest(session, sendResponse);
                return;
            }

            //Add the state information for the source
            RtpClient.TransportContext sourceContext = found.RtpClient.GetContextForMediaDescription(mediaDescription);

            //If the source has no TransportContext for that format or the source has not recieved a packet yet
            if (sourceContext == null || sourceContext.SynchronizationSourceIdentifier == 0)
            {
                //Stream is not yet ready
                ProcessInvalidRtspRequest(session, RtspStatusCode.PreconditionFailed, null, sendResponse);
                return;
            }

            //Create the response and initialize the sockets if required
            RtspMessage resp = session.ProcessSetup(request, found, sourceContext);

            //Send the response
            ProcessSendRtspMessage(resp, session, sendResponse);

            resp = null;
        }
Example #51
0
        /// <summary>
        /// Processes a RtspRequest based on the contents
        /// </summary>
        /// <param name="request">The rtsp Request</param>
        /// <param name="session">The client information</param>
        internal void ProcessRtspRequest(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            try
            {
                //Log the reqeust now
                if (Logger != null) Logger.LogRequest(request, session);

                //Ensure we have a session and request and that the server is still running and that the session is still contained in this server
                if (request == null || request.IsDisposed || session == null || session.IsDisposed)
                {
                    return;
                }//Ensure the session is added to the connected clients if it has not been already.
                else if (session.m_Contained == null) TryAddSession(session);

                //Check for session moved from another process.
                //else if (session.m_Contained != null && session.m_Contained != this) return;

                //Turtle power!
                if (request.MethodString == "REGISTER")
                {
                    //Send back turtle food.
                    ProcessInvalidRtspRequest(session, Rtsp.RtspStatusCode.BadRequest, session.RtspSocket.ProtocolType == ProtocolType.Tcp ? "FU_WILL_NEVER_SUPPORT_THIS$00\a" : string.Empty, sendResponse);

                    return;
                }

                //All requests need the CSeq
                if (false == request.ContainsHeader(RtspHeaders.CSeq))
                {
                    //Send back a BadRequest.
                    ProcessInvalidRtspRequest(session, Rtsp.RtspStatusCode.BadRequest, null, sendResponse);

                    return;
                }

                /*
                 12.37 Session

               This request and response header field identifies an RTSP session
               started by the media server in a SETUP response and concluded by
               TEARDOWN on the presentation URL. The session identifier is chosen by
               the media server (see Section 3.4). Once a client receives a Session
               identifier, it MUST return it for any request related to that
               session.  A server does not have to set up a session identifier if it
               has other means of identifying a session, such as dynamically
               generated URLs.
                 */

                //Determine if the request is specific to a session
                if (request.ContainsHeader(RtspHeaders.Session))
                {
                    //Determine what session is being acted on.
                    string requestedSessionId = request.GetHeader(RtspHeaders.Session);

                    //If there is a null or empty session id this request is invalid.
                    if (string.IsNullOrWhiteSpace(requestedSessionId))
                    {
                        //Send back a BadRequest.
                        ProcessInvalidRtspRequest(session, Rtsp.RtspStatusCode.BadRequest, null, sendResponse);

                        return;
                    }
                    else requestedSessionId = requestedSessionId.Trim();

                    //If the given session does not have a sessionId or does not match the sessionId requested.
                    if (session.SessionId != requestedSessionId)
                    {
                        //Find any session which has the given id.
                        IEnumerable<ClientSession> matches = GetSessions(requestedSessionId);

                        //Atttempt to get the correct session
                        ClientSession correctSession = matches.FirstOrDefault();//(c => false == c.IsDisconnected);

                        //If no session could be found by the given sessionId
                        if (correctSession == null)
                        {

                            //Indicate the session requested could not be found.
                            ProcessInvalidRtspRequest(session, RtspStatusCode.SessionNotFound, null, sendResponse);

                            return;
                        }
                        else //There was a session found by the given Id
                        {

                            //Indicate the last request of this session was as given
                            session.LastRequest = request;

                            //The LastResponse is updated to be the value of whatever the correctSessions LastResponse is
                            // session.LastResponse = correctSession.LastResponse;

                            //Process the request from the correctSession but do not send a response.
                            ProcessRtspRequest(request, correctSession, false);

                            session.LastResponse = correctSession.LastResponse;

                            //Take the created response and sent it to the new session using it as the last response.
                            ProcessSendRtspMessage(session.LastResponse, session, sendResponse);

                            return;
                        }
                    }
                }

                //Check for out of order or duplicate requests.
                if (session.LastRequest != null)
                {
                    //Out of order
                    if (request.CSeq < session.LastRequest.CSeq)
                    {
                        //Send back a BadRequest.
                        ProcessInvalidRtspRequest(session, Rtsp.RtspStatusCode.BadRequest, null, sendResponse);

                        return;
                    }
                }

                //Dispose any last request.
                if (session.LastRequest != null && false == session.LastRequest.IsDisposed) session.LastRequest.Dispose();

                //Synchronize the server and client since this is not a duplicate
                session.LastRequest = request;

                //Determine if we support what the client requests in `Require` Header
                if (request.ContainsHeader(RtspHeaders.Require))
                {
                    //Certain features are requried... tcp etc.
                    //Todo ProcessRequired(
                }

                //If there is a body and no content-length
                if (false == string.IsNullOrWhiteSpace(request.Body) && false == request.ContainsHeader(RtspHeaders.ContentLength))
                {
                    //Send back a BadRequest.
                    ProcessInvalidRtspRequest(session, Rtsp.RtspStatusCode.BadRequest, null, sendResponse);

                    return;
                }

                //Optional Checks

                //UserAgent
                if (RequireUserAgent && false == request.ContainsHeader(RtspHeaders.UserAgent))
                {
                    //Send back a BadRequest.
                    ProcessInvalidRtspRequest(session, Rtsp.RtspStatusCode.BadRequest, null, sendResponse);

                    return;
                }

                //Minor version reflects changes made to the protocol but not the 'general message parsing' `algorithm`

                //Thus, RTSP/2.4 is a lower version than RTSP/2.13, which in turn is lower than RTSP/12.3.
                //Leading zeros SHALL NOT be sent and MUST be ignored by recipients.

                //Version - Should check request.Version != Version and that earlier versions are supprted.
                if (request.Version > Version)
                {
                    //ConvertToMessage

                    ProcessInvalidRtspRequest(session, RtspStatusCode.RtspVersionNotSupported, null, sendResponse);
                    return;
                }

                //4.2.  RTSP IRI and URI, Draft requires 501 response for rtspu iri but not for udp sockets using a rtsp location....
                //Should check request.Location.Scheme to not be rtspu but it was allowed previously...

                //If any custom handlers were registered.
                if (m_RequestHandlers.Count > 0)
                {
                    //Determine if there is a custom handler for the mthod
                    RtspRequestHandler custom;

                    //If there is
                    if (m_RequestHandlers.TryGetValue(request.Method, out custom))
                    {
                        //Then create the response
                        RtspMessage response;

                        //By invoking the handler, if true is returned
                        if (custom(request, out response))
                        {
                            //Use the response created by the custom handler
                            ProcessSendRtspMessage(response, session, sendResponse);

                            //Return because the custom handler has handled the request.
                            return;
                        }
                    }
                }

                //From this point if Authrorization is required and the stream exists
                //The server will responsd with AuthorizationRequired when it should NOT have probably respoded with that at this point.
                //The problem is that RequiredCredentails uses a Uri format by ID.
                //We could get a stream and then respond accordingly but that is how it currently works and it allows probing of streams which is what not desirable in some cases
                //Thus we have to use the location of the request and see if RequiredCredentials has anything which matches root.
                //This would force everything to have some type of authentication which would also be applicable to all lower level streams in the uri in the credential cache.
                //I could also change up the semantic and make everything Uri based rather then locations
                //Then it would also be easier to make /audio only passwords etc.

                //When stopping only handle teardown and keep alives
                if (m_StopRequested && (request.Method != RtspMethod.TEARDOWN && request.Method != RtspMethod.GET_PARAMETER && request.Method != RtspMethod.OPTIONS))
                {
                    ProcessInvalidRtspRequest(session, RtspStatusCode.BadRequest, null, sendResponse);

                    return;
                }

                //Determine the handler for the request and process it
                switch (request.Method)
                {
                    case RtspMethod.ANNOUNCE:
                        {
                            ProcessInvalidRtspRequest(session, RtspStatusCode.MethodNotAllowed, null, sendResponse);
                            break;
                        }
                    case RtspMethod.OPTIONS:
                        {
                            ProcessRtspOptions(request, session, sendResponse);
                            break;
                        }
                    case RtspMethod.DESCRIBE:
                        {
                            ProcessRtspDescribe(request, session, sendResponse);
                            break;
                        }
                    case RtspMethod.SETUP:
                        {
                            ProcessRtspSetup(request, session, sendResponse);
                            break;
                        }
                    case RtspMethod.PLAY:
                        {
                            ProcessRtspPlay(request, session, sendResponse);
                            break;
                        }
                    case RtspMethod.RECORD:
                        {
                            ProcessRtspRecord(request, session, sendResponse);
                            break;
                        }
                    case RtspMethod.PAUSE:
                        {
                            ProcessRtspPause(request, session, sendResponse);
                            break;
                        }
                    case RtspMethod.TEARDOWN:
                        {
                            ProcessRtspTeardown(request, session, sendResponse);
                            break;
                        }
                    case RtspMethod.GET_PARAMETER:
                        {
                            ProcessGetParameter(request, session, sendResponse);
                            break;
                        }
                    case RtspMethod.SET_PARAMETER:
                        {
                            ProcessSetParameter(request, session, sendResponse);
                            break;
                        }
                    case RtspMethod.REDIRECT: //Client can't redirect a server
                        {
                            ProcessInvalidRtspRequest(session, RtspStatusCode.BadRequest, null, sendResponse);
                            break;
                        }
                    case RtspMethod.UNKNOWN:
                    default:
                        {
                            //Per 2.0 Draft
                            ProcessInvalidRtspRequest(session, RtspStatusCode.NotImplemented, null, sendResponse);
                            break;
                        }
                }
            }
            catch (Exception ex)
            {
                //Log it
                if (Logger != null) Logger.LogException(ex);
            }
        }
Example #52
0
        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <param name="session"></param>
        internal void ProcessRtspPlay(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            RtpSource found = FindStreamByLocation(request.Location) as RtpSource;

            if (found == null)
            {
                ProcessLocationNotFoundRtspRequest(session, sendResponse);
                return;
            }

            if (false == AuthenticateRequest(request, found))
            {
                ProcessAuthorizationRequired(found, session, sendResponse);
                return;
            }
            else if (false == found.Ready)
            {
                //Stream is not yet ready
                ProcessInvalidRtspRequest(session, RtspStatusCode.PreconditionFailed, null, sendResponse);
                return;
            }

            //New method...
            TryCreateResponse:

            try
            {
                RtspMessage resp = session.ProcessPlay(request, found);

                //Send the response to the client
                ProcessSendRtspMessage(resp, session, sendResponse);

                if (resp.StatusCode == RtspStatusCode.OK)
                {
                    //Take the range into account given.
                    session.ProcessPacketBuffer(found);
                }

                resp = null;
            }
            catch(Exception ex)
            {
                if(Logger != null) Logger.LogException(ex);

                if (ex is InvalidOperationException) goto TryCreateResponse;

                throw;

            }
        }
Example #53
0
 internal override void LogResponse(RtspMessage response, ClientSession session)
 {
     try { Logger.Log(string.Format(Format, response.RtspMessageType, response.CSeq, response.RtspStatusCode, session.Id, null)); }
     catch { throw; }
 }
Example #54
0
        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <param name="session"></param>
        internal void ProcessRtspPause(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            RtpSource found = FindStreamByLocation(request.Location) as RtpSource;

            if (found == null)
            {
                ProcessLocationNotFoundRtspRequest(session, sendResponse);
                return;
            }

            if (false == AuthenticateRequest(request, found))
            {
                ProcessAuthorizationRequired(found, session, sendResponse);
                return;
            }

            //session.m_RtpClient.m_WorkerThread.Priority = ThreadPriority.BelowNormal;

            var resp = session.ProcessPause(request, found);

            //Might need to add some headers
            ProcessSendRtspMessage(resp, session, sendResponse);

            resp = null;
        }
Example #55
0
 public RtspSession(RtspMessage request, RtspMessage response, bool shouldDispose = true)
     : this(response, shouldDispose)
 {
     LastRequest = request;
 }
Example #56
0
        /// <summary>
        /// Provides the Options this server supports
        /// </summary>
        /// <param name="request"></param>
        /// <param name="session"></param>
        internal void ProcessRtspOptions(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            //See if the location requires a certain stream
            if (request.Location != RtspMessage.Wildcard && false == string.IsNullOrWhiteSpace(request.Location.LocalPath) && request.Location.LocalPath.Length > 1)
            {
                IMedia found = FindStreamByLocation(request.Location);

                //No stream with name
                if (found == null)
                {
                    ProcessLocationNotFoundRtspRequest(session, sendResponse);
                    return;
                }

                //See if RECORD is supported?

            }

            //Check for additional options of the stream... e.g. allow recording or not

            RtspMessage resp = session.CreateRtspResponse(request);

            resp.SetHeader(RtspHeaders.Public, "OPTIONS, DESCRIBE, SETUP, PLAY, PAUSE, TEARDOWN, GET_PARAMETER");

            //Allow for Authorized
            resp.SetHeader(RtspHeaders.Allow, "ANNOUNCE, RECORD, SET_PARAMETER");

            //Add from handlers?
            //if(m_RequestHandlers.Count > 0) string.Join(" ", m_RequestHandlers.Keys.ToArray())

            //Should allow server to have certain options removed from this result
            //ClientSession.ProcessOptions

            ProcessSendRtspMessage(resp, session, sendResponse);

            resp = null;
        }
 internal abstract void LogResponse(RtspMessage response, ClientSession session);
Example #58
0
        /// <summary>
        /// Decribes the requested stream
        /// </summary>
        /// <param name="request"></param>
        /// <param name="session"></param>
        internal void ProcessRtspDescribe(RtspMessage request, ClientSession session, bool sendResponse = true)
        {
            if (request.Location == RtspMessage.Wildcard)
            {
                var resp = session.CreateRtspResponse(request);

                ProcessSendRtspMessage(resp, session, sendResponse);

                resp = null;
            }
            else
            {
                string acceptHeader = request[RtspHeaders.Accept];

                //If an Accept header was given it must reflect a content-type of SDP.
                if (false == string.IsNullOrWhiteSpace(acceptHeader)
                    &&
                    string.Compare(acceptHeader, Sdp.SessionDescription.MimeType, true, System.Globalization.CultureInfo.InvariantCulture) > 0)
                {
                    ProcessInvalidRtspRequest(session);
                    return;
                }

                RtpSource found = FindStreamByLocation(request.Location) as RtpSource;

                if (found == null)
                {
                    ProcessLocationNotFoundRtspRequest(session, sendResponse);
                    return;
                }

                if (false == AuthenticateRequest(request, found))
                {
                    ProcessAuthorizationRequired(found, session, sendResponse);
                    return;
                }

                if (false == found.Ready)
                {
                    ProcessInvalidRtspRequest(session, RtspStatusCode.PreconditionFailed, null, sendResponse);
                    return;
                }

                var resp = session.ProcessDescribe(request, found);

                ProcessSendRtspMessage(resp, session, sendResponse);

                resp = null;

            }
        }
Example #59
0
        /*
         10.7 TEARDOWN

        The TEARDOWN request stops the stream delivery for the given URI,
        freeing the resources associated with it. If the URI is the
        presentation URI for this presentation, any RTSP session identifier
        associated with the session is no longer valid. Unless all transport
        parameters are defined by the session description, a SETUP request
        has to be issued before the session can be played again.
         */
        internal RtspMessage ProcessTeardown(RtspMessage request, RtpSource source)
        {
            //Determine if this is for only a single track or the entire shebang
            if (false == Attached.ContainsValue(source)) return CreateRtspResponse(request, RtspStatusCode.BadRequest);

            //Determine if we have the track
            string track = request.Location.Segments.Last().Replace("/", string.Empty);

            Sdp.MediaType mediaType;

            //For a single track
            if (Enum.TryParse <Sdp.MediaType>(track, true, out mediaType))
            {
                //bool GetContextBySdpControlLine... out mediaDescription
                RtpClient.TransportContext sourceContext = source.RtpClient.GetTransportContexts().FirstOrDefault(sc => sc.MediaDescription.MediaType == mediaType);

                //Cannot teardown media because we can't find the track they are asking to tear down
                if (sourceContext == null)
                {
                    return CreateRtspResponse(request, RtspStatusCode.NotFound);
                }
                else
                {
                    RemoveMedia(sourceContext.MediaDescription);
                }
            }
            else //Tear down all streams
            {
                RemoveSource(source);
            }

            //Return the response
            return CreateRtspResponse(request);
        }
Example #60
0
        protected virtual void ProcessHttpRtspRequest(IAsyncResult iar)
        {
            if (iar == null) return;

            var context = m_HttpListner.EndGetContext(iar);

            m_HttpListner.BeginGetContext(new AsyncCallback(ProcessHttpRtspRequest), null);

            if (context == null) return;

            if (context.Request.AcceptTypes.Any(t => string.Compare(t, "application/x-rtsp-tunnelled") == 0))
            {
                if (context.Request.HasEntityBody)
                {
                    using (var ips = context.Request.InputStream)
                    {
                        long size = context.Request.ContentLength64;

                        byte[] buffer = new byte[size];

                        int offset = 0;

                        while (size > 0)
                        {
                            size -= offset += ips.Read(buffer, offset, (int)size);
                        }

                        //Ensure the message is really Rtsp
                        RtspMessage request = new RtspMessage(System.Convert.FromBase64String(context.Request.ContentEncoding.GetString(buffer)));

                        //Check for validity
                        if (request.MessageType != RtspMessageType.Invalid)
                        {
                            //Process the request when complete
                            if (request.IsComplete)
                            {
                                ProcessRtspRequest(request, CreateSession(null), false);

                                return;
                            }

                        }
                    }
                }
                else //No body
                {
                    RtspMessage request = new RtspMessage()
                    {
                        Location = new Uri(RtspMessage.ReliableTransport + "://" + context.Request.LocalEndPoint.Address.ToString() +  context.Request.RawUrl),
                        Method = RtspMethod.DESCRIBE
                    };

                    foreach (var header in context.Request.Headers.AllKeys)
                    {
                        request.SetHeader(header, context.Request.Headers[header]);
                    }

                    ProcessRtspRequest(request, CreateSession(null), false);
                }
            }
        }