Inheritance: Rtsp.Messages.RtspMessage
Example #1
0
        private void RTSP_ProcessPlayRequest(RtspRequestPlay message, RtspListener listener)
        {
            OnPlay?.Invoke(Id);

            Play = true;  // ACTUALLY YOU COULD PAUSE JUST THE VIDEO (or JUST THE AUDIO)
            _logger.Info($"Connection {Id} play started");

            string range    = "npt=0-";                                                  // Playing the 'video' from 0 seconds until the end
            string rtp_info = "url=" + message.RtspUri + ";seq=" + _videoSequenceNumber; // TODO Add rtptime  +";rtptime="+session.rtp_initial_timestamp;

            // Send the reply
            Rtsp.Messages.RtspResponse play_response = message.CreateResponse(_logger);
            play_response.AddHeader("Range: " + range);
            play_response.AddHeader("RTP-Info: " + rtp_info);
            listener.SendMessage(play_response);



            //TODO: find a p[lace for this check]
            // Session ID was not found in the list of Sessions. Send a 454 error

            /*   Rtsp.Messages.RtspResponse play_failed_response = (e.Message as Rtsp.Messages.RtspRequestPlay).CreateResponse();
             * play_failed_response.ReturnCode = 454; // Session Not Found
             * listener.SendMessage(play_failed_response);*/
        }
Example #2
0
        private void RTSP_ProcessAuthorization(RtspRequest message, RtspListener listener)
        {
            bool authorized = false;

            if (message.Headers.ContainsKey("Authorization") == true)
            {
                // The Header contained Authorization
                // Check the message has the correct Authorization
                // If it does not have the correct Authorization then close the RTSP connection
                authorized = _auth.IsValid(message);

                if (authorized == false)
                {
                    // Send a 401 Authentication Failed reply, then close the RTSP Socket
                    Rtsp.Messages.RtspResponse authorization_response = message.CreateResponse(_logger);
                    authorization_response.AddHeader("WWW-Authenticate: " + _auth.GetHeader());
                    authorization_response.ReturnCode = 401;
                    listener.SendMessage(authorization_response);

                    CloseConnection("unauthorized");
                    listener.Dispose();
                    return;
                }
            }
            if ((message.Headers.ContainsKey("Authorization") == false))
            {
                // Send a 401 Authentication Failed with extra info in WWW-Authenticate
                // to tell the Client if we are using Basic or Digest Authentication
                Rtsp.Messages.RtspResponse authorization_response = message.CreateResponse(_logger);
                authorization_response.AddHeader("WWW-Authenticate: " + _auth.GetHeader()); // 'Basic' or 'Digest'
                authorization_response.ReturnCode = 401;
                listener.SendMessage(authorization_response);
                return;
            }
        }
Example #3
0
        public void SetSession()
        {
            RtspResponse testObject = new RtspResponse();

            testObject.Session = "12345";

            Assert.AreEqual("12345", testObject.Headers[RtspHeaderNames.Session]);
        }
Example #4
0
        public void ReadSessionAndTimeout()
        {
            RtspResponse testObject = new RtspResponse();

            testObject.Headers[RtspHeaderNames.Session] = "12345;timeout=33";

            Assert.AreEqual("12345", testObject.Session);
            Assert.AreEqual(33, testObject.Timeout);
        }
Example #5
0
        /// <summary>
        /// Gets the assiociate OK response with the request.
        /// </summary>
        /// <returns>
        /// an Rtsp response corresponding to request.
        /// </returns>
        public override RtspResponse CreateResponse(ILogger logger)
        {
            RtspResponse response = base.CreateResponse(logger);

            // Add genric suported operations.
            response.Headers.Add(RtspHeaderNames.Public, "OPTIONS,DESCRIBE,SETUP,PLAY,PAUSE,TEARDOWN,GET_PARAMETER");

            return(response);
        }
Example #6
0
        public void SetSessionAndTimeout()
        {
            RtspResponse testObject = new RtspResponse();

            testObject.Session = "12345";
            testObject.Timeout = 10;

            Assert.AreEqual("12345;timeout=10", testObject.Headers[RtspHeaderNames.Session]);
        }
Example #7
0
        public void ChangeSession()
        {
            RtspResponse testObject = new RtspResponse();

            testObject.Headers[RtspHeaderNames.Session] = "12345;timeout=33";

            testObject.Session = "456";

            Assert.AreEqual("456", testObject.Session);
            Assert.AreEqual(33, testObject.Timeout);
            Assert.AreEqual("456;timeout=33", testObject.Headers[RtspHeaderNames.Session]);
        }
Example #8
0
        private void RTSP_ProcessTeardownRequest(RtspRequestTeardown message, RtspListener listener)
        {
            if (message.Session == _videoSessionId) // SHOULD HAVE AN AUDIO TEARDOWN AS WELL
            {
                // If this is UDP, close the transport
                // For TCP there is no transport to close (as RTP packets were interleaved into the RTSP connection)

                Rtsp.Messages.RtspResponse teardown_response = message.CreateResponse(_logger);
                listener.SendMessage(teardown_response);

                CloseConnection("teardown");
            }
        }
Example #9
0
        /// <summary>
        /// Gets the assiociate OK response with the request.
        /// </summary>
        /// <returns>an Rtsp response correcponding to request.</returns>
        public virtual RtspResponse CreateResponse(ILogger logger)
        {
            RtspResponse returnValue = new RtspResponse(logger);

            returnValue.ReturnCode = 200;
            returnValue.CSeq       = this.CSeq;
            if (this.Headers.ContainsKey(RtspHeaderNames.Session))
            {
                returnValue.Headers[RtspHeaderNames.Session] = this.Headers[RtspHeaderNames.Session];
            }

            return(returnValue);
        }
Example #10
0
        private void RTSP_ProcessOptionsRequest(RtspRequestOptions message, RtspListener listener)
        {
            String requested_url = message.RtspUri.ToString();

            _logger.Info($"Connection {listener.ConnectionId} requested for url: {requested_url}");

            _videoSource = _requestUrlVideoSourceResolverStrategy.ResolveVideoSource(requested_url);
            OnConnectionAdded?.Invoke(Id, _videoSource); //treat connection useful when VideoSource determined

            // Create the reponse to OPTIONS
            Rtsp.Messages.RtspResponse options_response = message.CreateResponse(_logger);
            // Rtsp.Messages.RtspResponse options_response = OnRtspMessageReceived?.Invoke(message as Rtsp.Messages.RtspRequest,targetConnection);
            listener.SendMessage(options_response);
        }
Example #11
0
        private void RTSP_ProcessPauseRequest(RtspRequestPause message, RtspListener listener)
        {
            if (message.Session == _videoSessionId /* OR AUDIO SESSION ID */)
            {
                OnStop?.Invoke(Id);

                // found the session
                Play = false; // COULD HAVE PLAY/PAUSE FOR VIDEO AND AUDIO
            }


            // ToDo - only send back the OK response if the Session in the RTSP message was found
            Rtsp.Messages.RtspResponse pause_response = message.CreateResponse(_logger);
            listener.SendMessage(pause_response);
        }
Example #12
0
        private void RTSP_ProcessDescribeRequest(RtspRequestDescribe message, RtspListener listener)
        {
            String requested_url = message.RtspUri.ToString();

            Task <byte[]> sdpDataTask = _videoSource != null?
                                        OnProvideSdpData?.Invoke(Id, _videoSource)
                                            : Task.FromResult <byte[]>(null);

            byte[] sdpData = sdpDataTask.Result;

            if (sdpData != null)
            {
                Rtsp.Messages.RtspResponse describe_response = message.CreateResponse(_logger);

                describe_response.AddHeader("Content-Base: " + requested_url);
                describe_response.AddHeader("Content-Type: application/sdp");
                describe_response.Data = sdpData;
                describe_response.AdjustContentLength();

                // Create the reponse to DESCRIBE
                // This must include the Session Description Protocol (SDP)

                describe_response.Headers.TryGetValue(RtspHeaderNames.ContentBase, out contentBase);

                using (StreamReader sdp_stream = new StreamReader(new MemoryStream(describe_response.Data)))
                {
                    _sdpFile = Rtsp.Sdp.SdpFile.Read(sdp_stream);
                }

                listener.SendMessage(describe_response);
            }
            else
            {
                Rtsp.Messages.RtspResponse describe_response = (message as Rtsp.Messages.RtspRequestDescribe).CreateResponse(_logger);
                //Method Not Valid In This State"
                describe_response.ReturnCode = 455;
                listener.SendMessage(describe_response);
            }
        }
Example #13
0
        /// <summary>
        /// Create the good type of Rtsp Message from the header.
        /// </summary>
        /// <param name="aRequestLine">A request line.</param>
        /// <returns>An Rtsp message</returns>
        public static RtspMessage GetRtspMessage(string aRequestLine, ILogger logger)
        {
            // We can't determine the message
            if (string.IsNullOrEmpty(aRequestLine))
            {
                return(new RtspMessage(logger));
            }
            string[]    requestParts = aRequestLine.Split(new char[] { ' ' }, 3);
            RtspMessage returnValue;

            if (requestParts.Length == 3)
            {
                // A request is : Method SP Request-URI SP RTSP-Version
                // A response is : RTSP-Version SP Status-Code SP Reason-Phrase
                // RTSP-Version = "RTSP" "/" 1*DIGIT "." 1*DIGIT
                if (_rtspVersionTest.IsMatch(requestParts[2]))
                {
                    returnValue = RtspRequest.GetRtspRequest(requestParts, logger);
                }
                else if (_rtspVersionTest.IsMatch(requestParts[0]))
                {
                    returnValue = new RtspResponse(logger);
                }
                else
                {
                    logger.Warn(CultureInfo.InvariantCulture, "Got a strange message {0}", aRequestLine);
                    returnValue = new RtspMessage(logger);
                }
            }
            else
            {
                logger.Warn(CultureInfo.InvariantCulture, "Got a strange message {0}", aRequestLine);
                returnValue = new RtspMessage(logger);
            }

            returnValue.Command = aRequestLine;
            return(returnValue);
        }
Example #14
0
        /// <summary>
        /// Create the good type of Rtsp Message from the header.
        /// </summary>
        /// <param name="aRequestLine">A request line.</param>
        /// <returns>An Rtsp message</returns>
        public static RtspMessage GetRtspMessage(string aRequestLine)
        {
            // We can't determine the message
            if (string.IsNullOrEmpty(aRequestLine))
            {
                return(new RtspMessage());
            }
            string[]    requestParts = aRequestLine.Split(new char[] { ' ' }, 3);
            RtspMessage returnValue;

            if (requestParts.Length == 3)
            {
                // A request is : Method SP Request-URI SP RTSP-Version
                // A response is : RTSP-Version SP Status-Code SP Reason-Phrase
                // RTSP-Version = "RTSP" "/" 1*DIGIT "." 1*DIGIT
                if (_rtspVersionTest.IsMatch(requestParts[2]))
                {
                    returnValue = RtspRequest.GetRtspRequest(requestParts);
                }
                else if (_rtspVersionTest.IsMatch(requestParts[0]))
                {
                    returnValue = new RtspResponse();
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine($"Got a strange message {aRequestLine}");
                    returnValue = new RtspMessage();
                }
            }
            else
            {
                System.Diagnostics.Debug.WriteLine($"Got a strange message {aRequestLine}");
                returnValue = new RtspMessage();
            }
            returnValue.Command = aRequestLine;
            return(returnValue);
        }
Example #15
0
    // Process each RTSP message that is received
    private void RTSP_Message_Received(object sender, RtspChunkEventArgs e)
    {
        // Cast the 'sender' and 'e' into the RTSP Listener (the Socket) and the RTSP Message
        Rtsp.RtspListener         listener = sender as Rtsp.RtspListener;
        Rtsp.Messages.RtspMessage message  = e.Message as Rtsp.Messages.RtspMessage;

        Console.WriteLine("RTSP message received " + message);


        // Check if the RTSP Message has valid authentication (validating against username,password,realm and nonce)
        if (auth != null)
        {
            bool authorized = false;
            if (message.Headers.ContainsKey("Authorization") == true)
            {
                // Check the message has the correct Authorization
                authorized = auth.IsValid(message);
            }
            if ((message.Headers.ContainsKey("Authorization") == false) ||
                authorized == false)
            {
                // Send a 401 Authentication Required reply
                Rtsp.Messages.RtspResponse authorization_response = (e.Message as Rtsp.Messages.RtspRequest).CreateResponse();
                authorization_response.AddHeader("WWW-Authenticate: " + auth.GetHeader());
                authorization_response.ReturnCode = 401;
                listener.SendMessage(authorization_response);
                return;
            }
        }

        // Handle OPTIONS message
        if (message is Rtsp.Messages.RtspRequestOptions)
        {
            // Create the reponse to OPTIONS
            Rtsp.Messages.RtspResponse options_response = (e.Message as Rtsp.Messages.RtspRequestOptions).CreateResponse();
            listener.SendMessage(options_response);
        }

        // Handle DESCRIBE message
        if (message is Rtsp.Messages.RtspRequestDescribe)
        {
            String requested_url = (message as Rtsp.Messages.RtspRequestDescribe).RtspUri.ToString();
            Console.WriteLine("Request for " + requested_url);

            // TODO. Check the requsted_url is valid. In this example we accept any RTSP URL

            //// Make the Base64 SPS and PPS
            //byte[] raw_sps = h264_encoder.GetRawSPS(); // no 0x00 0x00 0x00 0x01 or 32 bit size header
            //byte[] raw_pps = h264_encoder.GetRawPPS(); // no 0x00 0x00 0x00 0x01 or 32 bit size header
            //String sps_str = Convert.ToBase64String(raw_sps);
            //String pps_str = Convert.ToBase64String(raw_pps);

            StringBuilder sdp = new StringBuilder();

            // Generate the SDP
            // The sprop-parameter-sets provide the SPS and PPS for H264 video
            // The packetization-mode defines the H264 over RTP payloads used but is Optional
            sdp.Append("v=0\n");
            sdp.Append("o=user 123 0 IN IP4 0.0.0.0\n");
            sdp.Append("s=WPFRtspServer\n");
            sdp.Append("c=IN IP4 0.0.0.0\n");

            if (mStreams != null)
            {
                foreach (var item in mStreams)
                {
                    switch (item.Item1)
                    {
                    case StreamType.Video:
                    {
                        sdp.Append(string.Format("m=video 0 RTP/AVP {0}\n", (uint)item.Item1));
                        sdp.Append(string.Format("a=rtpmap:{0} {1}/90000\n", (uint)item.Item1, item.Item3));
                        sdp.Append(string.Format("a=fmtp:{0}\n", (uint)item.Item1));
                        sdp.Append(string.Format("a=control:trackID={0}\n", item.Item2));
                        //sdp.Append("a=fmtp:96 profile-level-id=42A01E; sprop-parameter-sets=" + sps_str + "," + pps_str + ";\n");
                    }
                    break;

                    case StreamType.Audio:
                    {
                        sdp.Append(string.Format("m=audio 0 RTP/AVP {0}\n", (uint)item.Item1));
                        sdp.Append(string.Format("a=rtpmap:{0} mpeg4-generic/90000/2\n", (uint)item.Item1));
                        sdp.Append(string.Format("a=fmtp:{0}\n", (uint)item.Item1));
                        sdp.Append(string.Format("a=control:trackID={0}\n", item.Item2));
                        //sdp.Append("a=fmtp:96 profile-level-id=42A01E; sprop-parameter-sets=" + sps_str + "," + pps_str + ";\n");
                    }
                    break;

                    case StreamType.None:
                    default:
                        break;
                    }
                }
            }

            byte[] sdp_bytes = Encoding.ASCII.GetBytes(sdp.ToString());

            // Create the reponse to DESCRIBE
            // This must include the Session Description Protocol (SDP)
            Rtsp.Messages.RtspResponse describe_response = (e.Message as Rtsp.Messages.RtspRequestDescribe).CreateResponse();

            describe_response.AddHeader("Content-Base: " + requested_url);
            describe_response.AddHeader("Content-Type: application/sdp");
            describe_response.Data = sdp_bytes;
            describe_response.AdjustContentLength();
            listener.SendMessage(describe_response);
        }

        // Handle SETUP message
        if (message is Rtsp.Messages.RtspRequestSetup)
        {
            //
            var setupMessage = message as Rtsp.Messages.RtspRequestSetup;

            // Check the RTSP transport
            // If it is UDP or Multicast, create the sockets
            // If it is RTP over RTSP we send data via the RTSP Listener

            // FIXME client may send more than one possible transport.
            // very rare
            Rtsp.Messages.RtspTransport transport = setupMessage.GetTransports()[0];


            // Construct the Transport: reply from the Server to the client
            Rtsp.Messages.RtspTransport transport_reply = new Rtsp.Messages.RtspTransport();
            transport_reply.SSrc = global_ssrc.ToString("X8"); // Convert to Hex, padded to 8 characters

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP)
            {
                // RTP over RTSP mode}
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.TCP;
                transport_reply.Interleaved    = new Rtsp.Messages.PortCouple(transport.Interleaved.First, transport.Interleaved.Second);
            }

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP &&
                transport.IsMulticast == false)
            {
                // RTP over UDP mode}
                // Create a pair of UDP sockets
                // Pass the Port of the two sockets back in the reply
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.UDP;
                transport_reply.IsMulticast    = false;
                transport_reply.ClientPort     = transport.ClientPort; // FIX
                                                                       // for now until implemented
                transport_reply = null;
            }

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP &&
                transport.IsMulticast == true)
            {
                // RTP over Multicast UDP mode}
                // Create a pair of UDP sockets in Multicast Mode
                // Pass the Ports of the two sockets back in the reply
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.UDP;
                transport_reply.IsMulticast    = true;
                transport_reply.Port           = new Rtsp.Messages.PortCouple(7000, 7001); // FIX

                // for now until implemented
                transport_reply = null;
            }


            if (transport_reply != null)
            {
                RTPSession new_session = new RTPSession();
                new_session.listener        = listener;
                new_session.sequence_number = (UInt16)rnd.Next(65535); // start with a random 16 bit sequence number
                new_session.ssrc            = global_ssrc;

                // Add the transports to the Session
                new_session.client_transport = transport;
                new_session.transport_reply  = transport_reply;

                lock (rtp_list)
                {
                    if (setupMessage.RtspUri.Segments != null && setupMessage.RtspUri.Segments.Length == 2)
                    {
                        var split = setupMessage.RtspUri.Segments[1].Split(new char[] { '=' });

                        if (split != null && split.Length == 2)
                        {
                            int.TryParse(split[1], out new_session.trackID);
                        }
                    }



                    //setupMessage.SourcePort.RemoteAdress

                    // Create a 'Session' and add it to the Session List
                    // ToDo - Check the Track ID. In the SDP the H264 video track is TrackID 0
                    // Place Lock() here so the Session Count and the addition to the list is locked
                    new_session.session_id = session_count.ToString();

                    // Add the new session to the Sessions List
                    rtp_list.Add(new_session);
                    session_count++;
                }


                Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse();
                setup_response.Headers[Rtsp.Messages.RtspHeaderNames.Transport] = transport_reply.ToString();
                setup_response.Session = new_session.session_id;
                listener.SendMessage(setup_response);
            }
            else
            {
                Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse();
                // unsuported transport
                setup_response.ReturnCode = 461;
                listener.SendMessage(setup_response);
            }
        }

        // Handle PLAY message
        if (message is Rtsp.Messages.RtspRequestPlay)
        {
            lock (rtp_list)
            {
                // Search for the Session in the Sessions List. Change the state of "PLAY"
                bool session_found = false;
                foreach (RTPSession session in rtp_list)
                {
                    //if (session.session_id.Equals(message.Session))
                    {
                        // found the session
                        session_found = true;
                        session.play  = true;

                        string range    = "npt=0-";                                                                                      // Playing the 'video' from 0 seconds until the end
                        string rtp_info = "url=" + ((Rtsp.Messages.RtspRequestPlay)message).RtspUri + ";seq=" + session.sequence_number; // TODO Add rtptime  +";rtptime="+session.rtp_initial_timestamp;

                        // Send the reply
                        Rtsp.Messages.RtspResponse play_response = (e.Message as Rtsp.Messages.RtspRequestPlay).CreateResponse();
                        play_response.AddHeader("Range: " + range);
                        play_response.AddHeader("RTP-Info: " + rtp_info);
                        listener.SendMessage(play_response);
                    }
                }

                if (session_found == false)
                {
                    // Session ID was not found in the list of Sessions. Send a 454 error
                    Rtsp.Messages.RtspResponse play_failed_response = (e.Message as Rtsp.Messages.RtspRequestPlay).CreateResponse();
                    play_failed_response.ReturnCode = 454; // Session Not Found
                    listener.SendMessage(play_failed_response);
                }
            }
        }

        // Handle PLAUSE message
        if (message is Rtsp.Messages.RtspRequestPause)
        {
            lock (rtp_list)
            {
                // Search for the Session in the Sessions List. Change the state of "PLAY"
                foreach (RTPSession session in rtp_list)
                {
                    if (session.session_id.Equals(message.Session))
                    {
                        // found the session
                        session.play = false;
                        break;
                    }
                }
            }

            // ToDo - only send back the OK response if the Session in the RTSP message was found
            Rtsp.Messages.RtspResponse pause_response = (e.Message as Rtsp.Messages.RtspRequestPause).CreateResponse();
            listener.SendMessage(pause_response);
        }


        // Handle GET_PARAMETER message, often used as a Keep Alive
        if (message is Rtsp.Messages.RtspRequestGetParameter)
        {
            // Create the reponse to GET_PARAMETER
            Rtsp.Messages.RtspResponse getparameter_response = (e.Message as Rtsp.Messages.RtspRequestGetParameter).CreateResponse();
            listener.SendMessage(getparameter_response);
        }


        // Handle TEARDOWN
        if (message is Rtsp.Messages.RtspRequestTeardown)
        {
            lock (rtp_list)
            {
                // Search for the Session in the Sessions List.
                foreach (RTPSession session in rtp_list.ToArray()) // Convert to ToArray so we can delete from the rtp_list
                {
                    if (session.session_id.Equals(message.Session))
                    {
                        // TODO - Close UDP or Multicast transport
                        // For TCP there is no transport to close
                        rtp_list.Remove(session);
                        // Close the RTSP socket
                        listener.Dispose();
                    }
                }
            }
        }
    }
Example #16
0
        private void RTSP_ProcessSetupRequest(RtspRequestSetup message, RtspListener listener)
        {
            //
            var setupMessage = message;

            // Check the RTSP transport
            // If it is UDP or Multicast, create the sockets
            // If it is RTP over RTSP we send data via the RTSP Listener

            // FIXME client may send more than one possible transport.
            // very rare
            RtspTransport transport = setupMessage.GetTransports()[0];


            // Construct the Transport: reply from the Server to the client
            Rtsp.UDPSocket udp_pair;
            RtspTransport  transport_reply   = RTSP_ConstructReplyTransport(transport, out udp_pair);
            bool           mediaTransportSet = false;

            if (transport_reply != null)
            {
                // Update the session with transport information
                String copy_of_session_id = "";

                // ToDo - Check the Track ID to determine if this is a SETUP for the Video Stream
                // or a SETUP for an Audio Stream.
                // In the SDP the H264 video track is TrackID 0

                // Add the transports to the connection

                if (contentBase != null)
                {
                    string controlTrack = setupMessage.RtspUri.AbsoluteUri.Replace(contentBase, string.Empty);
                    var    requestMedia = _sdpFile.Medias.FirstOrDefault(media =>
                                                                         media.Attributs.FirstOrDefault(a => a.Key == "control" &&
                                                                                                        (a.Value == controlTrack || "/" + a.Value == controlTrack)) != null);

                    if (requestMedia != null)
                    {
                        if (requestMedia.MediaType == Media.MediaTypes.video)
                        {
                            _videoClientTransport = transport;
                            _videoTransportReply  = transport_reply;

                            // If we are sending in UDP mode, add the UDP Socket pair and the Client Hostname
                            if (_videoUdpPair != null)
                            {
                                ReleaseUDPSocket(_videoUdpPair);
                            }
                            _videoUdpPair     = udp_pair;
                            mediaTransportSet = true;

                            if (setupMessage.Session == null)
                            {
                                _videoSessionId = _sessionHandle.ToString();
                                _sessionHandle++;
                            }
                            else
                            {
                                _videoSessionId = setupMessage.Session;
                            }



                            // Copy the Session ID
                            copy_of_session_id = _videoSessionId;
                        }

                        if (requestMedia.MediaType == Media.MediaTypes.audio)
                        {
                            _audioClientTransport = transport;
                            _audioTransportReply  = transport_reply;

                            // If we are sending in UDP mode, add the UDP Socket pair and the Client Hostname
                            if (_audioUdpPair != null)
                            {
                                ReleaseUDPSocket(_audioUdpPair);
                            }
                            _audioUdpPair     = udp_pair;
                            mediaTransportSet = true;


                            if (setupMessage.Session == null)
                            {
                                _audioSessionId = _sessionHandle.ToString();
                                _sessionHandle++;
                            }
                            else
                            {
                                _audioSessionId = setupMessage.Session;
                            }

                            // Copy the Session ID
                            copy_of_session_id = _audioSessionId;
                        }
                    }
                }

                if (false == mediaTransportSet)
                {
                    Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse(_logger);
                    // unsuported mediatime
                    setup_response.ReturnCode = 415;
                    listener.SendMessage(setup_response);
                }
                else
                {
                    Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse(_logger);
                    setup_response.Headers[Rtsp.Messages.RtspHeaderNames.Transport] = transport_reply.ToString();
                    setup_response.Session = copy_of_session_id;
                    setup_response.Timeout = timeout_in_seconds;
                    listener.SendMessage(setup_response);
                }
            }
            else
            {
                Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse(_logger);
                // unsuported transport
                setup_response.ReturnCode = 461;
                listener.SendMessage(setup_response);
            }

            if (false == mediaTransportSet)
            {
                if (udp_pair != null)
                {
                    ReleaseUDPSocket(udp_pair);
                    udp_pair = null;
                }
            }
        }
Example #17
0
        // RTSP Messages are OPTIONS, DESCRIBE, SETUP, PLAY etc
        private void Rtsp_MessageReceived(object sender, Rtsp.RtspChunkEventArgs e)
        {
            Rtsp.Messages.RtspResponse message = e.Message as Rtsp.Messages.RtspResponse;

            Console.WriteLine("Received " + message.OriginalRequest.ToString());

            // Check if the Message has an Authenticate header. If so we update the 'realm' and 'nonce'
            if (message.Headers.ContainsKey(RtspHeaderNames.WWWAuthenticate))
            {
                String www_authenticate = message.Headers[RtspHeaderNames.WWWAuthenticate];

                // Parse www_authenticate
                // EG:   Digest realm="AXIS_WS_ACCC8E3A0A8F", nonce="000057c3Y810622bff50b36005eb5efeae118626a161bf", stale=FALSE
                string[] items = www_authenticate.Split(new char[] { ',', ' ' });
                foreach (string item in items)
                {
                    // Split on the = symbol and load in
                    string[] parts = item.Split(new char[] { '=' });
                    if (parts.Count() >= 2 && parts[0].Trim().Equals("realm"))
                    {
                        realm = parts[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
                    }
                    else if (parts.Count() >= 2 && parts[0].Trim().Equals("nonce"))
                    {
                        nonce = parts[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
                    }
                }

                Console.WriteLine("WWW Authorize parsed for " + realm + " " + nonce);
            }


            // If we get a reply to OPTIONS and CSEQ is 1 (which was our first command), then send the DESCRIBE
            // If we fer a reply to OPTIONS and CSEQ is not 1, it must have been a keepalive command
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestOptions)
            {
                if (message.CSeq == 1)
                {
                    // Start a Timer to send an OPTIONS command (for keepalive) every 20 seconds
                    keepalive_timer          = new System.Timers.Timer();
                    keepalive_timer.Elapsed += Timer_Elapsed;
                    keepalive_timer.Interval = 20 * 1000;
                    keepalive_timer.Enabled  = true;

                    // send the DESCRIBE. First time around we have no WWW-Authorise
                    Rtsp.Messages.RtspRequest describe_message = new Rtsp.Messages.RtspRequestDescribe();
                    describe_message.RtspUri = new Uri(url);
                    rtsp_client.SendMessage(describe_message);
                }
                else
                {
                    // do nothing
                }
            }


            // If we get a reply to DESCRIBE (which was our second command), then prosess SDP and send the SETUP
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestDescribe)
            {
                // Got a reply for DESCRIBE

                // First time we send DESCRIBE we will not have the authorization Nonce so we
                // handle the Unauthorized 401 error here and send a new DESCRIBE message

                if (message.IsOk == false)
                {
                    Console.WriteLine("Got Error in DESCRIBE Reply " + message.ReturnCode + " " + message.ReturnMessage);

                    if (message.ReturnCode == 401 && (message.OriginalRequest.Headers.ContainsKey(RtspHeaderNames.Authorization) == false))
                    {
                        // Error 401 - Unauthorized, but the request did not use Authorizarion.

                        if (username == null || password == null)
                        {
                            // we do nothave a username or password. Abort
                            return;
                        }
                        // Send a new DESCRIBE with authorization
                        String digest_authorization = GenerateDigestAuthorization(username, password,
                                                                                  realm, nonce, url, "DESCRIBE");

                        Rtsp.Messages.RtspRequest describe_message = new Rtsp.Messages.RtspRequestDescribe();
                        describe_message.RtspUri = new Uri(url);
                        if (digest_authorization != null)
                        {
                            describe_message.Headers.Add(RtspHeaderNames.Authorization, digest_authorization);
                        }
                        rtsp_client.SendMessage(describe_message);
                        return;
                    }
                    else if (message.ReturnCode == 401 && (message.OriginalRequest.Headers.ContainsKey(RtspHeaderNames.Authorization) == true))
                    {
                        // Authorization failed
                        return;
                    }
                    else
                    {
                        // some other error
                        return;
                    }
                }

                // Examine the SDP

                Console.Write(System.Text.Encoding.UTF8.GetString(message.Data));

                Rtsp.Sdp.SdpFile sdp_data;
                using (StreamReader sdp_stream = new StreamReader(new MemoryStream(message.Data)))
                {
                    sdp_data = Rtsp.Sdp.SdpFile.Read(sdp_stream);
                }

                // Process each 'Media' Attribute in the SDP (each sub-stream)

                for (int x = 0; x < sdp_data.Medias.Count; x++)
                {
                    bool audio = (sdp_data.Medias[x].MediaType == Rtsp.Sdp.Media.MediaTypes.audio);
                    bool video = (sdp_data.Medias[x].MediaType == Rtsp.Sdp.Media.MediaTypes.video);

                    if (video && video_payload != -1)
                    {
                        continue;                               // have already matched an video payload
                    }
                    if (audio && audio_payload != -1)
                    {
                        continue;                               // have already matched an audio payload
                    }
                    if (audio || video)
                    {
                        // search the attributes for control, rtpmap and fmtp
                        // (fmtp only applies to video)
                        String control             = "";   // the "track" or "stream id"
                        Rtsp.Sdp.AttributFmtp fmtp = null; // holds SPS and PPS in base64 (h264 video)
                        foreach (Rtsp.Sdp.Attribut attrib in sdp_data.Medias[x].Attributs)
                        {
                            if (attrib.Key.Equals("control"))
                            {
                                String sdp_control = attrib.Value;
                                if (sdp_control.ToLower().StartsWith("rtsp://"))
                                {
                                    control = sdp_control; //absolute path
                                }
                                else
                                {
                                    control = url + "/" + sdp_control; // relative path
                                }
                            }
                            if (attrib.Key.Equals("fmtp"))
                            {
                                fmtp = attrib as Rtsp.Sdp.AttributFmtp;
                            }
                            if (attrib.Key.Equals("rtpmap"))
                            {
                                Rtsp.Sdp.AttributRtpMap rtpmap = attrib as Rtsp.Sdp.AttributRtpMap;

                                // Check if the Codec Used (EncodingName) is one we support
                                String[] valid_video_codecs = { "H264" };
                                String[] valid_audio_codecs = { "PCMA", "PCMU" };

                                if (video && Array.IndexOf(valid_video_codecs, rtpmap.EncodingName) >= 0)
                                {
                                    // found a valid codec
                                    video_codec   = rtpmap.EncodingName;
                                    video_payload = sdp_data.Medias[x].PayloadType;
                                }
                                if (audio && Array.IndexOf(valid_audio_codecs, rtpmap.EncodingName) >= 0)
                                {
                                    audio_codec   = rtpmap.EncodingName;
                                    audio_payload = sdp_data.Medias[x].PayloadType;
                                }
                            }
                        }

                        // If the rtpmap contains H264 then split the fmtp to get the sprop-parameter-sets which hold the SPS and PPS in base64
                        if (video && video_codec.Contains("H264") && fmtp != null)
                        {
                            var param   = Rtsp.Sdp.H264Parameters.Parse(fmtp.FormatParameter);
                            var sps_pps = param.SpropParameterSets;
                            if (sps_pps.Count() >= 2)
                            {
                                byte[] sps = sps_pps[0];
                                byte[] pps = sps_pps[1];
                                if (Received_SPS_PPS != null)
                                {
                                    Received_SPS_PPS(sps, pps);
                                }
                            }
                        }

                        // Send the SETUP RTSP command if we have a matching Payload Decoder
                        if (video && video_payload == -1)
                        {
                            continue;
                        }
                        if (audio && audio_payload == -1)
                        {
                            continue;
                        }

                        RtspTransport transport    = null;
                        int           data_channel = 0;
                        int           rtcp_channel = 0;

                        if (rtp_transport == RTP_TRANSPORT.TCP)
                        {
                            // Server interleaves the RTP packets over the RTSP connection
                            // Example for TCP mode (RTP over RTSP)   Transport: RTP/AVP/TCP;interleaved=0-1
                            if (video)
                            {
                                video_data_channel = 0;
                                video_rtcp_channel = 1;
                                data_channel       = video_data_channel;
                                rtcp_channel       = video_rtcp_channel;
                            }
                            if (audio)
                            {
                                audio_data_channel = 2;
                                audio_rtcp_channel = 3;
                                data_channel       = audio_data_channel;
                                rtcp_channel       = audio_rtcp_channel;
                            }
                            transport = new RtspTransport()
                            {
                                LowerTransport = RtspTransport.LowerTransportType.TCP,
                                Interleaved    = new PortCouple(data_channel, rtcp_channel), // Eg Channel 0 for video. Channel 1 for RTCP status reports
                            };
                        }
                        if (rtp_transport == RTP_TRANSPORT.UDP)
                        {
                            // Server sends the RTP packets to a Pair of UDP Ports (one for data, one for rtcp control messages)
                            // Example for UDP mode                   Transport: RTP/AVP;unicast;client_port=8000-8001
                            if (video)
                            {
                                video_data_channel = video_udp_pair.data_port;     // Used in DataReceived event handler
                                video_rtcp_channel = video_udp_pair.control_port;  // Used in DataReceived event handler
                                data_channel       = video_data_channel;
                                rtcp_channel       = video_rtcp_channel;
                            }
                            if (audio)
                            {
                                audio_data_channel = audio_udp_pair.data_port;     // Used in DataReceived event handler
                                audio_rtcp_channel = audio_udp_pair.control_port;  // Used in DataReceived event handler
                                data_channel       = audio_data_channel;
                                rtcp_channel       = audio_rtcp_channel;
                            }
                            transport = new RtspTransport()
                            {
                                LowerTransport = RtspTransport.LowerTransportType.UDP,
                                IsMulticast    = false,
                                ClientPort     = new PortCouple(data_channel, rtcp_channel), // a Channel for data (video or audio). a Channel for RTCP status reports
                            };
                        }
                        if (rtp_transport == RTP_TRANSPORT.MULTICAST)
                        {
                            // Server sends the RTP packets to a Pair of UDP ports (one for data, one for rtcp control messages)
                            // using Multicast Address and Ports that are in the reply to the SETUP message
                            // Example for MULTICAST mode     Transport: RTP/AVP;multicast
                            if (video)
                            {
                                video_data_channel = 0; // we get this information in the SETUP message reply
                                video_rtcp_channel = 0; // we get this information in the SETUP message reply
                            }
                            if (audio)
                            {
                                audio_data_channel = 0; // we get this information in the SETUP message reply
                                audio_rtcp_channel = 0; // we get this information in the SETUP message reply
                            }
                            transport = new RtspTransport()
                            {
                                LowerTransport = RtspTransport.LowerTransportType.UDP,
                                IsMulticast    = true
                            };
                        }

                        // Add authorization (if there is a username and password)
                        String digest_authorization = GenerateDigestAuthorization(username, password,
                                                                                  realm, nonce, url, "SETUP");

                        // Send SETUP
                        Rtsp.Messages.RtspRequestSetup setup_message = new Rtsp.Messages.RtspRequestSetup();
                        setup_message.RtspUri = new Uri(control);
                        setup_message.AddTransport(transport);
                        if (digest_authorization != null)
                        {
                            setup_message.Headers.Add("Authorization", digest_authorization);
                        }
                        rtsp_client.SendMessage(setup_message);
                    }
                }
            }


            // If we get a reply to SETUP (which was our third command), then process and then send PLAY
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestSetup)
            {
                // Got Reply to SETUP
                if (message.IsOk == false)
                {
                    Console.WriteLine("Got Error in SETUP Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                Console.WriteLine("Got reply from Setup. Session is " + message.Session);

                session = message.Session; // Session value used with Play, Pause, Teardown

                // Check the Transport header
                if (message.Headers.ContainsKey(RtspHeaderNames.Transport))
                {
                    RtspTransport transport = RtspTransport.Parse(message.Headers[RtspHeaderNames.Transport]);

                    // Check if Transport header includes Multicast
                    if (transport.IsMulticast)
                    {
                        String multicast_address = transport.Destination;
                        video_data_channel = transport.Port.First;
                        video_rtcp_channel = transport.Port.Second;

                        // Create the Pair of UDP Sockets in Multicast mode
                        video_udp_pair = new Rtsp.UDPSocket(multicast_address, video_data_channel, multicast_address, video_rtcp_channel);
                        video_udp_pair.DataReceived += Rtp_DataReceived;
                        video_udp_pair.Start();

                        // TODO - Need to set audio_udp_pair
                    }
                }

                // Send PLAY
                Rtsp.Messages.RtspRequest play_message = new Rtsp.Messages.RtspRequestPlay();
                play_message.RtspUri = new Uri(url);
                play_message.Session = session;
                rtsp_client.SendMessage(play_message);
            }

            // If we get a reply to PLAY (which was our fourth command), then we should have video being received
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestPlay)
            {
                // Got Reply to PLAY
                if (message.IsOk == false)
                {
                    Console.WriteLine("Got Error in PLAY Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                Console.WriteLine("Got reply from Play  " + message.Command);
            }
        }
Example #18
0
 /// <summary>
 /// Create the good type of Rtsp Message from the header.
 /// </summary>
 /// <param name="aRequestLine">A request line.</param>
 /// <returns>An Rtsp message</returns>
 public static RtspMessage GetRtspMessage(string aRequestLine)
 {
     // We can't determine the message
     if (string.IsNullOrEmpty(aRequestLine))
         return new RtspMessage();
     string[] requestParts = aRequestLine.Split(new char[] { ' ' }, 3);
     RtspMessage returnValue;
     if (requestParts.Length == 3)
     {
         // A request is : Method SP Request-URI SP RTSP-Version
         // A response is : RTSP-Version SP Status-Code SP Reason-Phrase
         // RTSP-Version = "RTSP" "/" 1*DIGIT "." 1*DIGIT
         if (_rtspVersionTest.IsMatch(requestParts[2]))
             returnValue = RtspRequest.GetRtspRequest(requestParts);
         else if (_rtspVersionTest.IsMatch(requestParts[0]))
             returnValue = new RtspResponse();
         else
         {
             _logger.Warn(CultureInfo.InvariantCulture, "Got a strange message {0}", aRequestLine);
             returnValue = new RtspMessage();
         }
     }
     else
     {
         _logger.Warn(CultureInfo.InvariantCulture, "Got a strange message {0}", aRequestLine);
         returnValue = new RtspMessage();
     }
     returnValue.Command = aRequestLine;
     return returnValue;
 }
Example #19
0
        /// <summary>
        /// Handles the response to a setup message.
        /// </summary>
        /// <param name="message">A response message.</param>
        private void HandleResponseToSetup(RtspResponse message)
        {
            RtspRequest original = message.OriginalRequest;
            string setupKey = original.SourcePort.RemoteAdress + "SEQ" + message.CSeq.ToString(CultureInfo.InvariantCulture);

            if (message.IsOk)
            {
                Forwarder forwarder = ConfigureTransportAndForwarder(message, _setupForwarder[setupKey]);

                RtspSession newSession;
                string sessionKey = RtspSession.GetSessionName(original.RtspUri, message.Session);
                if (_activesSession.ContainsKey(sessionKey))
                {
                    newSession = _activesSession[sessionKey];
                    _logger.Info("There was an already a session with ths ID {0}", newSession.Name);
                }
                else
                {
                    _logger.Info("Create a new session with the ID {0}", sessionKey);
                    newSession = new RtspSession();
                    newSession.Name = message.Session;
                    newSession.Destination = original.RtspUri.Authority;
                    _activesSession.Add(sessionKey, newSession);
                }

                newSession.AddForwarder(original.RtspUri, forwarder);
                newSession.Timeout = message.Timeout;
            }
            // No needed here anymore.
            _setupForwarder.Remove(setupKey);
        }
Example #20
0
        // RTSP Messages are OPTIONS, DESCRIBE, SETUP, PLAY etc
        private void Rtsp_MessageReceived(object sender, Rtsp.RtspChunkEventArgs e)
        {
            Rtsp.Messages.RtspResponse message = e.Message as Rtsp.Messages.RtspResponse;

            Console.WriteLine("Received " + message.OriginalRequest.ToString());

            // If we get a reply to OPTIONS and CSEQ is 1 (which was our first command), then send the DESCRIBE
            // If we fer a reply to OPTIONS and CSEQ is not 1, it must have been a keepalive command
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestOptions)
            {
                if (message.CSeq == 1)
                {
                    // Start a Timer to send an OPTIONS command (for keepalive) every 20 seconds
                    keepalive_timer          = new System.Timers.Timer();
                    keepalive_timer.Elapsed += Timer_Elapsed;
                    keepalive_timer.Interval = 20 * 1000;
                    keepalive_timer.Enabled  = true;

                    // send the Describe
                    Rtsp.Messages.RtspRequest describe_message = new Rtsp.Messages.RtspRequestDescribe();
                    describe_message.RtspUri = new Uri(url);
                    rtsp_client.SendMessage(describe_message);
                }
                else
                {
                    // do nothing
                }
            }


            // If we get a reply to DESCRIBE (which was our second command), then prosess SDP and send the SETUP
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestDescribe)
            {
                // Got a reply for DESCRIBE
                // Examine the SDP

                Console.Write(System.Text.Encoding.UTF8.GetString(message.Data));

                Rtsp.Sdp.SdpFile sdp_data;
                using (StreamReader sdp_stream = new StreamReader(new MemoryStream(message.Data)))
                {
                    sdp_data = Rtsp.Sdp.SdpFile.Read(sdp_stream);
                }

                // Process each 'Media' Attribute in the SDP (each sub-stream)
                // If the attribute is for Video, then carry out a SETUP and a PLAY
                // Only do this for the first Video attribute in case there is more than one in the SDP

                for (int x = 0; x < sdp_data.Medias.Count; x++)
                {
                    if (sdp_data.Medias[x].GetMediaType() == Rtsp.Sdp.Media.MediaType.video)
                    {
                        // We only want the first video sub-stream
                        if (video_payload == -1)
                        {
                            // seach the atributes for control, fmtp and rtpmap
                            String control = "";                   // the "track" or "stream id"
                            Rtsp.Sdp.AttributFmtp   fmtp   = null; // holds SPS and PPS in base64
                            Rtsp.Sdp.AttributRtpMap rtpmap = null; // holds Payload format, eg 96 often used with H264 as first dynamic payload value
                            foreach (Rtsp.Sdp.Attribut attrib in sdp_data.Medias[x].Attributs)
                            {
                                if (attrib.Key.Equals("control"))
                                {
                                    control = attrib.Value;
                                }
                                if (attrib.Key.Equals("fmtp"))
                                {
                                    fmtp = attrib as Rtsp.Sdp.AttributFmtp;
                                }
                                if (attrib.Key.Equals("rtpmap"))
                                {
                                    rtpmap = attrib as Rtsp.Sdp.AttributRtpMap;
                                }
                            }

                            // Split the fmtp to get the sprop-parameter-sets which hold the SPS and PPS in base64
                            if (fmtp != null)
                            {
                                var param   = Rtsp.Sdp.H264Parameters.Parse(fmtp.FormatParameter);
                                var sps_pps = param.SpropParameterSets;
                                if (sps_pps.Count > 0)
                                {
                                    video_sps = sps_pps[0];
                                }
                                if (sps_pps.Count > 1)
                                {
                                    video_pps = sps_pps[1];
                                }
                                Output_NAL(sps_pps); // output SPS and PPS
                            }



                            // Split the rtpmap to get the Payload Type
                            video_payload = 0;
                            if (rtpmap != null)
                            {
                                video_payload = rtpmap.PayloadNumber;
                            }


                            Rtsp.Messages.RtspRequestSetup setup_message = new Rtsp.Messages.RtspRequestSetup();
                            setup_message.RtspUri = new Uri(url + "/" + control);

                            RtspTransport transport = null;
                            if (rtp_transport == RTP_TRANSPORT.TCP)
                            {
                                // Server interleaves the RTP packets over the RTSP connection
                                // Example for TCP mode (RTP over RTSP)   Transport: RTP/AVP/TCP;interleaved=0-1
                                video_data_channel = 0;  // Used in DataReceived event handler
                                video_rtcp_channel = 1;  // Used in DataReceived event handler
                                transport          = new RtspTransport()
                                {
                                    LowerTransport = RtspTransport.LowerTransportType.TCP,
                                    Interleaved    = new PortCouple(video_data_channel, video_rtcp_channel), // Channel 0 for video. Channel 1 for RTCP status reports
                                };
                            }
                            if (rtp_transport == RTP_TRANSPORT.UDP)
                            {
                                // Server sends the RTP packets to a Pair of UDP Ports (one for data, one for rtcp control messages)
                                // Example for UDP mode                   Transport: RTP/AVP;unicast;client_port=8000-8001
                                video_data_channel = udp_pair.data_port;     // Used in DataReceived event handler
                                video_rtcp_channel = udp_pair.control_port;  // Used in DataReceived event handler
                                transport          = new RtspTransport()
                                {
                                    LowerTransport = RtspTransport.LowerTransportType.UDP,
                                    IsMulticast    = false,
                                    ClientPort     = new PortCouple(video_data_channel, video_rtcp_channel), // a Channel for video. a Channel for RTCP status reports
                                };
                            }
                            if (rtp_transport == RTP_TRANSPORT.MULTICAST)
                            {
                                // Server sends the RTP packets to a Pair of UDP ports (one for data, one for rtcp control messages)
                                // using Multicast Address and Ports that are in the reply to the SETUP message
                                // Example for MULTICAST mode     Transport: RTP/AVP;multicast
                                video_data_channel = 0; // we get this information in the SETUP message reply
                                video_rtcp_channel = 0; // we get this information in the SETUP message reply
                                transport          = new RtspTransport()
                                {
                                    LowerTransport = RtspTransport.LowerTransportType.UDP,
                                    IsMulticast    = true
                                };
                            }
                            setup_message.AddTransport(transport);

                            rtsp_client.SendMessage(setup_message);
                        }
                    }
                }
            }


            // If we get a reply to SETUP (which was our third command), then process then send PLAY
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestSetup)
            {
                // Got Reply to SETUP
                Console.WriteLine("Got reply from Setup. Session is " + message.Session);

                session = message.Session; // Session value used with Play, Pause, Teardown

                // Check the Transport header
                if (message.Headers.ContainsKey(RtspHeaderNames.Transport))
                {
                    RtspTransport transport = RtspTransport.Parse(message.Headers[RtspHeaderNames.Transport]);

                    // Check if Transport header includes Multicast
                    if (transport.IsMulticast)
                    {
                        String multicast_address = transport.Destination;
                        video_data_channel = transport.Port.First;
                        video_rtcp_channel = transport.Port.Second;

                        // Create the Pair of UDP Sockets in Multicast mode
                        udp_pair = new UDPSocket(multicast_address, video_data_channel, multicast_address, video_rtcp_channel);
                        udp_pair.DataReceived += Rtp_DataReceived;
                        udp_pair.Start();
                    }
                }

                Rtsp.Messages.RtspRequest play_message = new Rtsp.Messages.RtspRequestPlay();
                play_message.RtspUri = new Uri(url);
                play_message.Session = session;
                rtsp_client.SendMessage(play_message);
            }

            // If we get a reply to PLAY (which was our fourth command), then we should have video being received
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestPlay)
            {
                // Got Reply to PLAY
                Console.WriteLine("Got reply from Play  " + message.Command);
            }
        }
Example #21
0
        // RTSP Messages are OPTIONS, DESCRIBE, SETUP, PLAY etc
        private void Rtsp_MessageReceived(object sender, Rtsp.RtspChunkEventArgs e)
        {
            Rtsp.Messages.RtspResponse message = e.Message as Rtsp.Messages.RtspResponse;

            Console.WriteLine("Received " + message.OriginalRequest.ToString());

            // Check if the Message has an Authenticate header and what type it is
            if (message.Headers.ContainsKey(RtspHeaderNames.WWWAuthenticate))
            {
                String www_authenticate = message.Headers[RtspHeaderNames.WWWAuthenticate];

                // Parse www_authenticate
                // EG:   WWW-Authenticate: Basic realm="xxxxxxx"
                // EG:   WWW-Authenticate: Digest realm="AXIS_WS_ACCC8E3A0A8F", nonce="000057c3Y810622bff50b36005eb5efeae118626a161bf", stale=FALSE
                string[] items = www_authenticate.Split(new char[] { ',', ' ' });  // split on Comma and Space

                // Process the first item
                if (items.Count() >= 1 && items[0].Equals("Basic"))
                {
                    authentication = AUTHENTICATION.BASIC;
                }
                else if (items.Count() >= 1 && items[0].Equals("Digest"))
                {
                    authentication = AUTHENTICATION.DIGEST;
                }

                // Process the remaining items
                for (int i = 1; i < items.Count(); i++)
                {
                    string[] parts = items[i].Split(new char[] { '=' }); // Split on Equals
                    if (parts.Count() >= 2 && parts[0].Trim().Equals("realm"))
                    {
                        realm = parts[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
                    }
                    else if (parts.Count() >= 2 && parts[0].Trim().Equals("nonce"))
                    {
                        nonce = parts[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
                    }
                }
            }


            // If we get a reply to OPTIONS and CSEQ is 1 (which was our first command), then send the DESCRIBE
            // If we fer a reply to OPTIONS and CSEQ is not 1, it must have been a keepalive command
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestOptions)
            {
                if (message.CSeq == 1)
                {
                    // Start a Timer to send an OPTIONS command (for keepalive) every 20 seconds
                    keepalive_timer          = new System.Timers.Timer();
                    keepalive_timer.Elapsed += Timer_Elapsed;
                    keepalive_timer.Interval = 20 * 1000;
                    keepalive_timer.Enabled  = true;

                    // send the DESCRIBE. First time around we have no WWW-Authorise
                    Rtsp.Messages.RtspRequest describe_message = new Rtsp.Messages.RtspRequestDescribe();
                    describe_message.RtspUri = new Uri(url);
                    rtsp_client.SendMessage(describe_message);
                }
                else
                {
                    // do nothing
                }
            }


            // If we get a reply to DESCRIBE (which was our second command), then prosess SDP and send the SETUP
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestDescribe)
            {
                // Got a reply for DESCRIBE

                // First time we send DESCRIBE we do not add any authorization (and we could not add it even if we wanted to
                // as we will not have the authorization Nonce value required for Digest mode
                // So we have to handle the Unauthorized 401 error here and send a new DESCRIBE message

                if (message.IsOk == false)
                {
                    Console.WriteLine("Got Error in DESCRIBE Reply " + message.ReturnCode + " " + message.ReturnMessage);

                    if (message.ReturnCode == 401 && (message.OriginalRequest.Headers.ContainsKey(RtspHeaderNames.Authorization) == false))
                    {
                        // Error 401 - Unauthorized, but the original request did not use Authorization so try again with Authorization added

                        if (username == null || password == null)
                        {
                            // we do nothave a username or password. Abort
                            return;
                        }
                        // Send a new DESCRIBE with authorization
                        Rtsp.Messages.RtspRequest describe_message = new Rtsp.Messages.RtspRequestDescribe();
                        describe_message.RtspUri = new Uri(url);
                        if (authentication != AUTHENTICATION.NONE)
                        {
                            String authorization_string = GenerateAuthorization(username, password, authentication,
                                                                                realm, nonce, url, "DESCRIBE");
                            if (authorization_string != null)
                            {
                                describe_message.Headers.Add("Authorization", authorization_string);
                            }
                        }
                        rtsp_client.SendMessage(describe_message);
                        return;
                    }
                    else if (message.ReturnCode == 401 && (message.OriginalRequest.Headers.ContainsKey(RtspHeaderNames.Authorization) == true))
                    {
                        // Authorization failed
                        return;
                    }
                    else
                    {
                        // some other error
                        return;
                    }
                }

                // Examine the SDP

                Console.Write(System.Text.Encoding.UTF8.GetString(message.Data));

                Rtsp.Sdp.SdpFile sdp_data;
                using (StreamReader sdp_stream = new StreamReader(new MemoryStream(message.Data)))
                {
                    sdp_data = Rtsp.Sdp.SdpFile.Read(sdp_stream);
                }

                // Process each 'Media' Attribute in the SDP (each sub-stream)
                // If the attribute is for Video, then carry out a SETUP and a PLAY
                // Only do this for the first Video attribute in case there is more than one in the SDP

                for (int x = 0; x < sdp_data.Medias.Count; x++)
                {
                    if (sdp_data.Medias[x].MediaType == Rtsp.Sdp.Media.MediaTypes.video)
                    {
                        // We only want the first video sub-stream
                        if (video_payload == -1)
                        {
                            video_payload = sdp_data.Medias[x].PayloadType;

                            // search the attributes for control, fmtp and rtpmap
                            String control = "";                   // the "track" or "stream id"
                            Rtsp.Sdp.AttributFmtp   fmtp   = null; // holds SPS and PPS in base64 (h264)
                            Rtsp.Sdp.AttributRtpMap rtpmap = null; // custom payload (>=96) details
                            foreach (Rtsp.Sdp.Attribut attrib in sdp_data.Medias[x].Attributs)
                            {
                                if (attrib.Key.Equals("control"))
                                {
                                    String sdp_control = attrib.Value;
                                    if (sdp_control.ToLower().StartsWith("rtsp://"))
                                    {
                                        control = sdp_control; //absolute path
                                    }
                                    else
                                    {
                                        control = url + "/" + sdp_control; // relative path
                                    }
                                }
                                if (attrib.Key.Equals("fmtp"))
                                {
                                    fmtp = attrib as Rtsp.Sdp.AttributFmtp;
                                }
                                if (attrib.Key.Equals("rtpmap"))
                                {
                                    rtpmap = attrib as Rtsp.Sdp.AttributRtpMap;
                                }
                            }

                            // If the rtpmap contains H264 then split the fmtp to get the sprop-parameter-sets which hold the SPS and PPS in base64
                            if (rtpmap != null && rtpmap.Value.Contains("H264") && fmtp != null)
                            {
                                video_codec = "H264";
                                var param   = Rtsp.Sdp.H264Parameters.Parse(fmtp.FormatParameter);
                                var sps_pps = param.SpropParameterSets;
                                if (sps_pps.Count() >= 2)
                                {
                                    byte[] sps = sps_pps[0];
                                    byte[] pps = sps_pps[1];
                                    Output_SPS_PPS(sps, pps); // output SPS and PPS
                                }
                            }

                            RtspTransport transport = null;
                            if (rtp_transport == RTP_TRANSPORT.TCP)
                            {
                                // Server interleaves the RTP packets over the RTSP connection
                                // Example for TCP mode (RTP over RTSP)   Transport: RTP/AVP/TCP;interleaved=0-1
                                video_data_channel = 0;  // Used in DataReceived event handler
                                video_rtcp_channel = 1;  // Used in DataReceived event handler
                                transport          = new RtspTransport()
                                {
                                    LowerTransport = RtspTransport.LowerTransportType.TCP,
                                    Interleaved    = new PortCouple(video_data_channel, video_rtcp_channel), // Channel 0 for video. Channel 1 for RTCP status reports
                                };
                            }
                            if (rtp_transport == RTP_TRANSPORT.UDP)
                            {
                                // Server sends the RTP packets to a Pair of UDP Ports (one for data, one for rtcp control messages)
                                // Example for UDP mode                   Transport: RTP/AVP;unicast;client_port=8000-8001
                                video_data_channel = udp_pair.data_port;     // Used in DataReceived event handler
                                video_rtcp_channel = udp_pair.control_port;  // Used in DataReceived event handler
                                transport          = new RtspTransport()
                                {
                                    LowerTransport = RtspTransport.LowerTransportType.UDP,
                                    IsMulticast    = false,
                                    ClientPort     = new PortCouple(video_data_channel, video_rtcp_channel), // a Channel for video. a Channel for RTCP status reports
                                };
                            }
                            if (rtp_transport == RTP_TRANSPORT.MULTICAST)
                            {
                                // Server sends the RTP packets to a Pair of UDP ports (one for data, one for rtcp control messages)
                                // using Multicast Address and Ports that are in the reply to the SETUP message
                                // Example for MULTICAST mode     Transport: RTP/AVP;multicast
                                video_data_channel = 0; // we get this information in the SETUP message reply
                                video_rtcp_channel = 0; // we get this information in the SETUP message reply
                                transport          = new RtspTransport()
                                {
                                    LowerTransport = RtspTransport.LowerTransportType.UDP,
                                    IsMulticast    = true
                                };
                            }

                            // Send SETUP
                            Rtsp.Messages.RtspRequestSetup setup_message = new Rtsp.Messages.RtspRequestSetup();
                            setup_message.RtspUri = new Uri(control);
                            setup_message.AddTransport(transport);
                            if (authentication != AUTHENTICATION.NONE)
                            {
                                String authorization_string = GenerateAuthorization(username, password, authentication,
                                                                                    realm, nonce, url, "SETUP");
                                if (authorization_string != null)
                                {
                                    setup_message.Headers.Add("Authorization", authorization_string);
                                }
                            }
                            rtsp_client.SendMessage(setup_message);
                        }
                    }
                }
            }


            // If we get a reply to SETUP (which was our third command), then process and then send PLAY
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestSetup)
            {
                // Got Reply to SETUP
                if (message.IsOk == false)
                {
                    Console.WriteLine("Got Error in SETUP Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                Console.WriteLine("Got reply from Setup. Session is " + message.Session);

                session = message.Session; // Session value used with Play, Pause, Teardown

                // Check the Transport header
                if (message.Headers.ContainsKey(RtspHeaderNames.Transport))
                {
                    RtspTransport transport = RtspTransport.Parse(message.Headers[RtspHeaderNames.Transport]);

                    // Check if Transport header includes Multicast
                    if (transport.IsMulticast)
                    {
                        String multicast_address = transport.Destination;
                        video_data_channel = transport.Port.First;
                        video_rtcp_channel = transport.Port.Second;

                        // Create the Pair of UDP Sockets in Multicast mode
                        udp_pair = new UDPSocket(multicast_address, video_data_channel, multicast_address, video_rtcp_channel);
                        udp_pair.DataReceived += Rtp_DataReceived;
                        udp_pair.Start();
                    }
                }

                // Send PLAY
                Rtsp.Messages.RtspRequest play_message = new Rtsp.Messages.RtspRequestPlay();
                play_message.RtspUri = new Uri(url);
                play_message.Session = session;
                if (authentication != AUTHENTICATION.NONE)
                {
                    String authorization_string = GenerateAuthorization(username, password, authentication,
                                                                        realm, nonce, url, "PLAY");
                    if (authorization_string != null)
                    {
                        play_message.Headers.Add("Authorization", authorization_string);
                    }
                }

                rtsp_client.SendMessage(play_message);
            }

            // If we get a reply to PLAY (which was our fourth command), then we should have video being received
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestPlay)
            {
                // Got Reply to PLAY
                if (message.IsOk == false)
                {
                    Console.WriteLine("Got Error in PLAY Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                Console.WriteLine("Got reply from Play  " + message.Command);
            }
        }
Example #22
0
 private void RTSP_ProcessGetParameterRequest(RtspRequestGetParameter message, RtspListener listener)
 {
     // Create the reponse to GET_PARAMETER
     Rtsp.Messages.RtspResponse getparameter_response = message.CreateResponse(_logger);
     listener.SendMessage(getparameter_response);
 }
Example #23
0
    // Process each RTSP message that is received
    private void RTSP_Message_Received(object sender, RtspChunkEventArgs e)
    {
        // Cast the 'sender' and 'e' into the RTSP Listener (the Socket) and the RTSP Message
        Rtsp.RtspListener         listener = sender as Rtsp.RtspListener;
        Rtsp.Messages.RtspMessage message  = e.Message as Rtsp.Messages.RtspMessage;

        Console.WriteLine("RTSP message received " + message);


        // Check if the RTSP Message has valid authentication (validating against username,password,realm and nonce)
        if (auth != null)
        {
            bool authorized = false;
            if (message.Headers.ContainsKey("Authorization") == true)
            {
                // Check the message has the correct Authorization
                authorized = auth.IsValid(message);
            }
            if ((message.Headers.ContainsKey("Authorization") == false) ||
                authorized == false)
            {
                // Send a 401 Authentication Required reply
                Rtsp.Messages.RtspResponse authorization_response = (e.Message as Rtsp.Messages.RtspRequest).CreateResponse();
                authorization_response.AddHeader("WWW-Authenticate: " + auth.GetHeader());
                authorization_response.ReturnCode = 401;
                listener.SendMessage(authorization_response);
                return;
            }
        }

        // Update the RTSP Keepalive Timeout
        // We could check that the message is GET_PARAMETER or OPTIONS for a keepalive but instead we will update the timer on any message
        lock (rtsp_list)
        {
            foreach (RTSPConnection connection in rtsp_list)
            {
                if (connection.listener.RemoteAdress.Equals(listener.RemoteAdress))
                {
                    // found the connection
                    connection.time_since_last_rtsp_keepalive = DateTime.UtcNow;
                    break;
                }
            }
        }


        // Handle OPTIONS message
        if (message is Rtsp.Messages.RtspRequestOptions)
        {
            // Create the reponse to OPTIONS
            Rtsp.Messages.RtspResponse options_response = (e.Message as Rtsp.Messages.RtspRequestOptions).CreateResponse();
            listener.SendMessage(options_response);
        }

        // Handle DESCRIBE message
        if (message is Rtsp.Messages.RtspRequestDescribe)
        {
            String requested_url = (message as Rtsp.Messages.RtspRequestDescribe).RtspUri.ToString();
            Console.WriteLine("Request for " + requested_url);

            // TODO. Check the requsted_url is valid. In this example we accept any RTSP URL

            // Make the Base64 SPS and PPS
            byte[] raw_sps = h264_encoder.GetRawSPS(); // no 0x00 0x00 0x00 0x01 or 32 bit size header
            byte[] raw_pps = h264_encoder.GetRawPPS(); // no 0x00 0x00 0x00 0x01 or 32 bit size header
            String sps_str = Convert.ToBase64String(raw_sps);
            String pps_str = Convert.ToBase64String(raw_pps);

            StringBuilder sdp = new StringBuilder();

            // Generate the SDP
            // The sprop-parameter-sets provide the SPS and PPS for H264 video
            // The packetization-mode defines the H264 over RTP payloads used but is Optional
            sdp.Append("v=0\n");
            sdp.Append("o=user 123 0 IN IP4 0.0.0.0\n");
            sdp.Append("s=SharpRTSP Test Camera\n");
            sdp.Append("m=video 0 RTP/AVP 96\n");
            sdp.Append("c=IN IP4 0.0.0.0\n");
            sdp.Append("a=control:trackID=0\n");
            sdp.Append("a=rtpmap:96 H264/90000\n");
            sdp.Append("a=fmtp:96 profile-level-id=42A01E; sprop-parameter-sets=" + sps_str + "," + pps_str + ";\n");

            byte[] sdp_bytes = Encoding.ASCII.GetBytes(sdp.ToString());

            // Create the reponse to DESCRIBE
            // This must include the Session Description Protocol (SDP)
            Rtsp.Messages.RtspResponse describe_response = (e.Message as Rtsp.Messages.RtspRequestDescribe).CreateResponse();

            describe_response.AddHeader("Content-Base: " + requested_url);
            describe_response.AddHeader("Content-Type: application/sdp");
            describe_response.Data = sdp_bytes;
            describe_response.AdjustContentLength();
            listener.SendMessage(describe_response);
        }

        // Handle SETUP message
        if (message is Rtsp.Messages.RtspRequestSetup)
        {
            //
            var setupMessage = message as Rtsp.Messages.RtspRequestSetup;

            // Check the RTSP transport
            // If it is UDP or Multicast, create the sockets
            // If it is RTP over RTSP we send data via the RTSP Listener

            // FIXME client may send more than one possible transport.
            // very rare
            Rtsp.Messages.RtspTransport transport = setupMessage.GetTransports()[0];


            // Construct the Transport: reply from the Server to the client
            Rtsp.Messages.RtspTransport transport_reply = new Rtsp.Messages.RtspTransport();
            transport_reply.SSrc = global_ssrc.ToString("X8"); // Convert to Hex, padded to 8 characters

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP)
            {
                // RTP over RTSP mode}
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.TCP;
                transport_reply.Interleaved    = new Rtsp.Messages.PortCouple(transport.Interleaved.First, transport.Interleaved.Second);
            }

            Rtsp.UDPSocket udp_pair = null;
            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP &&
                transport.IsMulticast == false)
            {
                Boolean udp_supported = true;
                if (udp_supported)
                {
                    // RTP over UDP mode
                    // Create a pair of UDP sockets - One is for the Video, one is for the RTCP
                    udp_pair = new Rtsp.UDPSocket(50000, 51000); // give a range of 500 pairs (1000 addresses) to try incase some address are in use
                    udp_pair.DataReceived += (object local_sender, RtspChunkEventArgs local_e) => {
                        // RTCP data received
                        Console.WriteLine("RTCP data received " + local_sender.ToString() + " " + local_e.ToString());
                    };
                    udp_pair.Start(); // start listening for data on the UDP ports

                    // Pass the Port of the two sockets back in the reply
                    transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.UDP;
                    transport_reply.IsMulticast    = false;
                    transport_reply.ClientPort     = new Rtsp.Messages.PortCouple(udp_pair.data_port, udp_pair.control_port);
                }
                else
                {
                    transport_reply = null;
                }
            }

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP &&
                transport.IsMulticast == true)
            {
                // RTP over Multicast UDP mode}
                // Create a pair of UDP sockets in Multicast Mode
                // Pass the Ports of the two sockets back in the reply
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.UDP;
                transport_reply.IsMulticast    = true;
                transport_reply.Port           = new Rtsp.Messages.PortCouple(7000, 7001); // FIX

                // for now until implemented
                transport_reply = null;
            }


            if (transport_reply != null)
            {
                // Update the session with transport information
                String copy_of_session_id = "";
                lock (rtsp_list)
                {
                    foreach (RTSPConnection connection in rtsp_list)
                    {
                        if (connection.listener.RemoteAdress.Equals(listener.RemoteAdress))
                        {
                            // ToDo - Check the Track ID to determine if this is a SETUP for the Video Stream
                            // or a SETUP for an Audio Stream.
                            // In the SDP the H264 video track is TrackID 0


                            // found the connection
                            // Add the transports to the connection
                            connection.video_client_transport = transport;
                            connection.video_transport_reply  = transport_reply;

                            // If we are sending in UDP mode, add the UDP Socket pair and the Client Hostname
                            connection.video_udp_pair = udp_pair;


                            connection.video_session_id = session_handle.ToString();
                            session_handle++;


                            // Copy the Session ID
                            copy_of_session_id = connection.video_session_id;
                            break;
                        }
                    }
                }

                Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse();
                setup_response.Headers[Rtsp.Messages.RtspHeaderNames.Transport] = transport_reply.ToString();
                setup_response.Session = copy_of_session_id;
                listener.SendMessage(setup_response);
            }
            else
            {
                Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse();
                // unsuported transport
                setup_response.ReturnCode = 461;
                listener.SendMessage(setup_response);
            }
        }

        // Handle PLAY message (Sent with a Session ID)
        if (message is Rtsp.Messages.RtspRequestPlay)
        {
            lock (rtsp_list)
            {
                // Search for the Session in the Sessions List. Change the state to "PLAY"
                bool session_found = false;
                foreach (RTSPConnection connection in rtsp_list)
                {
                    if (message.Session == connection.video_session_id) /* OR AUDIO_SESSION_ID */
                    {
                        // found the session
                        session_found   = true;
                        connection.play = true;                                                                                                   // ACTUALLY YOU COULD PAUSE JUST THE VIDEO (or JUST THE AUDIO)

                        string range    = "npt=0-";                                                                                               // Playing the 'video' from 0 seconds until the end
                        string rtp_info = "url=" + ((Rtsp.Messages.RtspRequestPlay)message).RtspUri + ";seq=" + connection.video_sequence_number; // TODO Add rtptime  +";rtptime="+session.rtp_initial_timestamp;

                        // Send the reply
                        Rtsp.Messages.RtspResponse play_response = (e.Message as Rtsp.Messages.RtspRequestPlay).CreateResponse();
                        play_response.AddHeader("Range: " + range);
                        play_response.AddHeader("RTP-Info: " + rtp_info);
                        listener.SendMessage(play_response);

                        break;
                    }
                }

                if (session_found == false)
                {
                    // Session ID was not found in the list of Sessions. Send a 454 error
                    Rtsp.Messages.RtspResponse play_failed_response = (e.Message as Rtsp.Messages.RtspRequestPlay).CreateResponse();
                    play_failed_response.ReturnCode = 454; // Session Not Found
                    listener.SendMessage(play_failed_response);
                }
            }
        }

        // Handle PAUSE message (Sent with a Session ID)
        if (message is Rtsp.Messages.RtspRequestPause)
        {
            lock (rtsp_list)
            {
                // Search for the Session in the Sessions List. Change the state of "PLAY"
                foreach (RTSPConnection connection in rtsp_list)
                {
                    if (message.Session == connection.video_session_id /* OR AUDIO SESSION ID */)
                    {
                        // found the session
                        connection.play = false; // COULD HAVE PLAY/PAUSE FOR VIDEO AND AUDIO
                        break;
                    }
                }
            }

            // ToDo - only send back the OK response if the Session in the RTSP message was found
            Rtsp.Messages.RtspResponse pause_response = (e.Message as Rtsp.Messages.RtspRequestPause).CreateResponse();
            listener.SendMessage(pause_response);
        }


        // Handle GET_PARAMETER message, often used as a Keep Alive
        if (message is Rtsp.Messages.RtspRequestGetParameter)
        {
            // Create the reponse to GET_PARAMETER
            Rtsp.Messages.RtspResponse getparameter_response = (e.Message as Rtsp.Messages.RtspRequestGetParameter).CreateResponse();
            listener.SendMessage(getparameter_response);
        }


        // Handle TEARDOWN (sent with a Session ID)
        if (message is Rtsp.Messages.RtspRequestTeardown)
        {
            lock (rtsp_list)
            {
                // Search for the Session in the Sessions List.
                foreach (RTSPConnection connection in rtsp_list.ToArray()) // Convert to ToArray so we can delete from the rtp_list
                {
                    if (message.Session == connection.video_session_id)    // SHOULD HAVE AN AUDIO TEARDOWN AS WELL
                    {
                        // If this is UDP, close the transport
                        // For TCP there is no transport to close (as RTP packets were interleaved into the RTSP connection)
                        if (connection.video_udp_pair != null)
                        {
                            connection.video_udp_pair.Stop();
                            connection.video_udp_pair = null;
                        }

                        rtsp_list.Remove(connection);

                        // Close the RTSP socket
                        listener.Dispose();
                    }
                }
            }
        }
    }
Example #24
0
    // Process each RTSP message that is received
    private void RTSP_Message_Received(object sender, RtspChunkEventArgs e)
    {
        // Cast the 'sender' and 'e' into the RTSP Listener (the Socket) and the RTSP Message
        Rtsp.RtspListener         listener = sender as Rtsp.RtspListener;
        Rtsp.Messages.RtspMessage message  = e.Message as Rtsp.Messages.RtspMessage;

        Console.WriteLine("RTSP message received " + message);

        // Handle OPTIONS message
        if (message is Rtsp.Messages.RtspRequestOptions)
        {
            // Create the reponse to OPTIONS
            Rtsp.Messages.RtspResponse options_response = (e.Message as Rtsp.Messages.RtspRequestOptions).CreateResponse();
            listener.SendMessage(options_response);
        }

        // Handle DESCRIBE message
        if (message is Rtsp.Messages.RtspRequestDescribe)
        {
            String requested_url = (message as Rtsp.Messages.RtspRequestDescribe).RtspUri.ToString();
            Console.WriteLine("Request for " + requested_url);

            // TODO. Check the requsted_url is valid. In this example we accept any RTSP URL

            // Make the Base64 SPS and PPS
            byte[] raw_sps = h264_encoder.GetRawSPS(); // no 0x00 0x00 0x00 0x01 or 32 bit size header
            byte[] raw_pps = h264_encoder.GetRawPPS(); // no 0x00 0x00 0x00 0x01 or 32 bit size header
            String sps_str = Convert.ToBase64String(raw_sps);
            String pps_str = Convert.ToBase64String(raw_pps);

            StringBuilder sdp = new StringBuilder();

            // Generate the SDP
            // The sprop-parameter-sets provide the SPS and PPS for H264 video
            // The packetization-mode defines the H264 over RTP payloads used but is Optional
            sdp.Append("v=0\n");
            sdp.Append("o=user 123 0 IN IP4 0.0.0.0\n");
            sdp.Append("s=SharpRTSP Test Camera\n");
            sdp.Append("m=video 0 RTP/AVP 96\n");
            sdp.Append("c=IN IP4 0.0.0.0\n");
            sdp.Append("a=control:trackID=0\n");
            sdp.Append("a=rtpmap:96 H264/90000\n");
            sdp.Append("a=fmtp:96 profile-level-id=42A01E; sprop-parameter-sets=" + sps_str + "," + pps_str + ";\n");

            byte[] sdp_bytes = Encoding.ASCII.GetBytes(sdp.ToString());

            // Create the reponse to DESCRIBE
            // This must include the Session Description Protocol (SDP)
            Rtsp.Messages.RtspResponse describe_response = (e.Message as Rtsp.Messages.RtspRequestDescribe).CreateResponse();

            describe_response.AddHeader("Content-Base: " + requested_url);
            describe_response.AddHeader("Content-Type: application/sdp");
            describe_response.Data = sdp_bytes;
            describe_response.AdjustContentLength();
            listener.SendMessage(describe_response);
        }

        // Handle SETUP message
        if (message is Rtsp.Messages.RtspRequestSetup)
        {
            //
            var setupMessage = message as Rtsp.Messages.RtspRequestSetup;

            // Check the RTSP transport
            // If it is UDP or Multicast, create the sockets
            // If it is RTP over RTSP we send data via the RTSP Listener

            // FIXME client may send more than one possible transport.
            // very rare
            Rtsp.Messages.RtspTransport transport = setupMessage.GetTransports()[0];


            // Construct the Transport: reply from the Server to the client
            Rtsp.Messages.RtspTransport transport_reply = new Rtsp.Messages.RtspTransport();

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP)
            {
                // RTP over RTSP mode}
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.TCP;
                transport_reply.Interleaved    = new Rtsp.Messages.PortCouple(transport.Interleaved.First, transport.Interleaved.Second);
            }

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP &&
                transport.IsMulticast == false)
            {
                // RTP over UDP mode}
                // Create a pair of UDP sockets
                // Pass the Port of the two sockets back in the reply
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.UDP;
                transport_reply.IsMulticast    = false;
                transport_reply.ClientPort     = transport.ClientPort; // FIX
                                                                       // for now until implemented
                transport_reply = null;
            }

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP &&
                transport.IsMulticast == true)
            {
                // RTP over Multicast UDP mode}
                // Create a pair of UDP sockets in Multicast Mode
                // Pass the Ports of the two sockets back in the reply
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.UDP;
                transport_reply.IsMulticast    = true;
                transport_reply.Port           = new Rtsp.Messages.PortCouple(7000, 7001); // FIX

                // for now until implemented
                transport_reply = null;
            }


            if (transport_reply != null)
            {
                RTPSession new_session = new RTPSession();
                new_session.listener        = listener;
                new_session.sequence_number = (UInt16)rnd.Next(65535); // start with a random 16 bit sequence number
                new_session.ssrc            = 1;

                // Add the transports to the Session
                new_session.client_transport = transport;
                new_session.transport_reply  = transport_reply;

                lock (rtp_list)
                {
                    // Create a 'Session' and add it to the Session List
                    // ToDo - Check the Track ID. In the SDP the H264 video track is TrackID 0
                    // Place Lock() here so the Session Count and the addition to the list is locked
                    new_session.session_id = session_count.ToString();

                    // Add the new session to the Sessions List
                    rtp_list.Add(new_session);
                    session_count++;
                }


                Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse();
                setup_response.Headers[Rtsp.Messages.RtspHeaderNames.Transport] = transport_reply.ToString();
                setup_response.Session = new_session.session_id;
                listener.SendMessage(setup_response);
            }
            else
            {
                Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse();
                // unsuported transport
                setup_response.ReturnCode = 461;
                listener.SendMessage(setup_response);
            }
        }

        // Handle PLAY message
        if (message is Rtsp.Messages.RtspRequestPlay)
        {
            lock (rtp_list)
            {
                // Search for the Session in the Sessions List. Change the state of "PLAY"
                foreach (RTPSession session in rtp_list)
                {
                    if (session.session_id.Equals(message.Session))
                    {
                        // found the session
                        session.play = true;
                        break;
                    }
                }
            }

            // ToDo - only send back the OK response if the Session in the RTSP message was found
            Rtsp.Messages.RtspResponse play_response = (e.Message as Rtsp.Messages.RtspRequestPlay).CreateResponse();
            listener.SendMessage(play_response);
        }

        // Handle PLAUSE message
        if (message is Rtsp.Messages.RtspRequestPause)
        {
            lock (rtp_list)
            {
                // Search for the Session in the Sessions List. Change the state of "PLAY"
                foreach (RTPSession session in rtp_list)
                {
                    if (session.session_id.Equals(message.Session))
                    {
                        // found the session
                        session.play = false;
                        break;
                    }
                }
            }

            // ToDo - only send back the OK response if the Session in the RTSP message was found
            Rtsp.Messages.RtspResponse pause_response = (e.Message as Rtsp.Messages.RtspRequestPause).CreateResponse();
            listener.SendMessage(pause_response);
        }


        // Handle GET_PARAMETER message, often used as a Keep Alive
        if (message is Rtsp.Messages.RtspRequestGetParameter)
        {
            // Create the reponse to GET_PARAMETER
            Rtsp.Messages.RtspResponse getparameter_response = (e.Message as Rtsp.Messages.RtspRequestGetParameter).CreateResponse();
            listener.SendMessage(getparameter_response);
        }


        // Handle TEARDOWN
        if (message is Rtsp.Messages.RtspRequestTeardown)
        {
            lock (rtp_list)
            {
                // Search for the Session in the Sessions List.
                foreach (RTPSession session in rtp_list.ToArray()) // Convert to ToArray so we can delete from the rtp_list
                {
                    if (session.session_id.Equals(message.Session))
                    {
                        // TODO - Close UDP or Multicast transport
                        // For TCP there is no transport to close
                        rtp_list.Remove(session);
                        // Close the RTSP socket
                        listener.Dispose();
                    }
                }
            }
        }
    }
Example #25
0
        // RTSP Messages are OPTIONS, DESCRIBE, SETUP, PLAY etc
        private void Rtsp_MessageReceived(object sender, Rtsp.RtspChunkEventArgs e)
        {
            Rtsp.Messages.RtspResponse message = e.Message as Rtsp.Messages.RtspResponse;

            Console.WriteLine("Received " + message.OriginalRequest.ToString());

            // If message has a 401 - Unauthorised Error, then we re-send the message with Authorization
            // using the most recently received 'realm' and 'nonce'
            if (message.IsOk == false)
            {
                Console.WriteLine("Got Error in RTSP Reply " + message.ReturnCode + " " + message.ReturnMessage);

                if (message.ReturnCode == 401 && (message.OriginalRequest.Headers.ContainsKey(RtspHeaderNames.Authorization) == true))
                {
                    // the authorization failed.
                    Stop();
                    return;
                }

                // Check if the Reply has an Authenticate header.
                if (message.ReturnCode == 401 && message.Headers.ContainsKey(RtspHeaderNames.WWWAuthenticate))
                {
                    // Process the WWW-Authenticate header
                    // EG:   Basic realm="AProxy"
                    // EG:   Digest realm="AXIS_WS_ACCC8E3A0A8F", nonce="000057c3Y810622bff50b36005eb5efeae118626a161bf", stale=FALSE

                    String   www_authenticate = message.Headers[RtspHeaderNames.WWWAuthenticate];
                    string[] items            = www_authenticate.Split(new char[] { ',', ' ' });

                    foreach (string item in items)
                    {
                        if (item.ToLower().Equals("basic"))
                        {
                            auth_type = "Basic";
                        }
                        else if (item.ToLower().Equals("digest"))
                        {
                            auth_type = "Digest";
                        }
                        else
                        {
                            // Split on the = symbol and update the realm and nonce
                            string[] parts = item.Split(new char[] { '=' }, 2);                          // max 2 parts in the results array
                            if (parts.Count() >= 2 && parts[0].Trim().Equals("realm"))
                            {
                                realm = parts[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
                            }
                            else if (parts.Count() >= 2 && parts[0].Trim().Equals("nonce"))
                            {
                                nonce = parts[1].Trim(new char[] { ' ', '\"' }); // trim space and quotes
                            }
                        }
                    }

                    Console.WriteLine("WWW Authorize parsed for " + auth_type + " " + realm + " " + nonce);
                }

                RtspMessage resend_message = message.OriginalRequest.Clone() as RtspMessage;

                if (auth_type != null)
                {
                    AddAuthorization(resend_message, username, password, auth_type, realm, nonce, url);
                }

                rtsp_client.SendMessage(resend_message);

                return;
            }


            // If we get a reply to OPTIONS then start the Keepalive Timer and send DESCRIBE
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestOptions)
            {
                if (keepalive_timer == null)
                {
                    // Start a Timer to send an OPTIONS command (for keepalive) every 20 seconds
                    keepalive_timer          = new System.Timers.Timer();
                    keepalive_timer.Elapsed += Timer_Elapsed;
                    keepalive_timer.Interval = 20 * 1000;
                    keepalive_timer.Enabled  = true;

                    // Send DESCRIBE
                    Rtsp.Messages.RtspRequest describe_message = new Rtsp.Messages.RtspRequestDescribe();
                    describe_message.RtspUri = new Uri(url);
                    if (auth_type != null)
                    {
                        AddAuthorization(describe_message, username, password, auth_type, realm, nonce, url);
                    }
                    rtsp_client.SendMessage(describe_message);
                }
                else
                {
                    // do nothing
                }
            }


            // If we get a reply to DESCRIBE (which was our second command), then prosess SDP and send the SETUP
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestDescribe)
            {
                // Got a reply for DESCRIBE
                if (message.IsOk == false)
                {
                    Console.WriteLine("Got Error in DESCRIBE Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                // Examine the SDP

                Console.Write(System.Text.Encoding.UTF8.GetString(message.Data));

                Rtsp.Sdp.SdpFile sdp_data;
                using (StreamReader sdp_stream = new StreamReader(new MemoryStream(message.Data)))
                {
                    sdp_data = Rtsp.Sdp.SdpFile.Read(sdp_stream);
                }

                // RTP and RTCP 'channels' are used in TCP Interleaved mode (RTP over RTSP)
                int next_free_rtp_channel  = 0;
                int next_free_rtcp_channel = 1;

                // Process each 'Media' Attribute in the SDP (each sub-stream)

                for (int x = 0; x < sdp_data.Medias.Count; x++)
                {
                    bool audio = (sdp_data.Medias[x].MediaType == Rtsp.Sdp.Media.MediaTypes.audio);
                    bool video = (sdp_data.Medias[x].MediaType == Rtsp.Sdp.Media.MediaTypes.video);

                    if (video && video_payload != -1)
                    {
                        continue;                               // have already matched an video payload
                    }
                    if (audio && audio_payload != -1)
                    {
                        continue;                               // have already matched an audio payload
                    }
                    if (audio || video)
                    {
                        // search the attributes for control, rtpmap and fmtp
                        // (fmtp only applies to video)
                        String control             = "";   // the "track" or "stream id"
                        Rtsp.Sdp.AttributFmtp fmtp = null; // holds SPS and PPS in base64 (h264 video)
                        foreach (Rtsp.Sdp.Attribut attrib in sdp_data.Medias[x].Attributs)
                        {
                            if (attrib.Key.Equals("control"))
                            {
                                String sdp_control = attrib.Value;
                                if (sdp_control.ToLower().StartsWith("rtsp://"))
                                {
                                    control = sdp_control; //absolute path
                                }
                                else
                                {
                                    control = url + "/" + sdp_control; // relative path
                                }
                            }
                            if (attrib.Key.Equals("fmtp"))
                            {
                                fmtp = attrib as Rtsp.Sdp.AttributFmtp;
                            }
                            if (attrib.Key.Equals("rtpmap"))
                            {
                                Rtsp.Sdp.AttributRtpMap rtpmap = attrib as Rtsp.Sdp.AttributRtpMap;

                                // Check if the Codec Used (EncodingName) is one we support
                                String[] valid_video_codecs = { "H264" };
                                String[] valid_audio_codecs = { "PCMA", "PCMU", "AMR" };

                                if (video && Array.IndexOf(valid_video_codecs, rtpmap.EncodingName) >= 0)
                                {
                                    // found a valid codec
                                    video_codec   = rtpmap.EncodingName;
                                    video_payload = sdp_data.Medias[x].PayloadType;
                                }
                                if (audio && Array.IndexOf(valid_audio_codecs, rtpmap.EncodingName) >= 0)
                                {
                                    audio_codec   = rtpmap.EncodingName;
                                    audio_payload = sdp_data.Medias[x].PayloadType;
                                }
                            }
                        }

                        // If the rtpmap contains H264 then split the fmtp to get the sprop-parameter-sets which hold the SPS and PPS in base64
                        if (video && video_codec.Contains("H264") && fmtp != null)
                        {
                            var param   = Rtsp.Sdp.H264Parameters.Parse(fmtp.FormatParameter);
                            var sps_pps = param.SpropParameterSets;
                            if (sps_pps.Count() >= 2)
                            {
                                byte[] sps = sps_pps[0];
                                byte[] pps = sps_pps[1];
                                if (Received_SPS_PPS != null)
                                {
                                    Received_SPS_PPS(sps, pps);
                                }
                            }
                        }

                        // Send the SETUP RTSP command if we have a matching Payload Decoder
                        if (video && video_payload == -1)
                        {
                            continue;
                        }
                        if (audio && audio_payload == -1)
                        {
                            continue;
                        }

                        RtspTransport transport = null;

                        if (rtp_transport == RTP_TRANSPORT.TCP)
                        {
                            // Server interleaves the RTP packets over the RTSP connection
                            // Example for TCP mode (RTP over RTSP)   Transport: RTP/AVP/TCP;interleaved=0-1
                            if (video)
                            {
                                video_data_channel = next_free_rtp_channel;
                                video_rtcp_channel = next_free_rtcp_channel;
                            }
                            if (audio)
                            {
                                audio_data_channel = next_free_rtp_channel;
                                audio_rtcp_channel = next_free_rtcp_channel;
                            }
                            transport = new RtspTransport()
                            {
                                LowerTransport = RtspTransport.LowerTransportType.TCP,
                                Interleaved    = new PortCouple(next_free_rtp_channel, next_free_rtcp_channel), // Eg Channel 0 for RTP video data. Channel 1 for RTCP status reports
                            };

                            next_free_rtp_channel  += 2;
                            next_free_rtcp_channel += 2;
                        }
                        if (rtp_transport == RTP_TRANSPORT.UDP)
                        {
                            int rtp_port  = 0;
                            int rtcp_port = 0;
                            // Server sends the RTP packets to a Pair of UDP Ports (one for data, one for rtcp control messages)
                            // Example for UDP mode                   Transport: RTP/AVP;unicast;client_port=8000-8001
                            if (video)
                            {
                                video_data_channel = video_udp_pair.data_port;     // Used in DataReceived event handler
                                video_rtcp_channel = video_udp_pair.control_port;  // Used in DataReceived event handler
                                rtp_port           = video_udp_pair.data_port;
                                rtcp_port          = video_udp_pair.control_port;
                            }
                            if (audio)
                            {
                                audio_data_channel = audio_udp_pair.data_port;     // Used in DataReceived event handler
                                audio_rtcp_channel = audio_udp_pair.control_port;  // Used in DataReceived event handler
                                rtp_port           = audio_udp_pair.data_port;
                                rtcp_port          = audio_udp_pair.control_port;
                            }
                            transport = new RtspTransport()
                            {
                                LowerTransport = RtspTransport.LowerTransportType.UDP,
                                IsMulticast    = false,
                                ClientPort     = new PortCouple(rtp_port, rtcp_port), // a UDP Port for data (video or audio). a UDP Port for RTCP status reports
                            };
                        }
                        if (rtp_transport == RTP_TRANSPORT.MULTICAST)
                        {
                            // Server sends the RTP packets to a Pair of UDP ports (one for data, one for rtcp control messages)
                            // using Multicast Address and Ports that are in the reply to the SETUP message
                            // Example for MULTICAST mode     Transport: RTP/AVP;multicast
                            if (video)
                            {
                                video_data_channel = 0; // we get this information in the SETUP message reply
                                video_rtcp_channel = 0; // we get this information in the SETUP message reply
                            }
                            if (audio)
                            {
                                audio_data_channel = 0; // we get this information in the SETUP message reply
                                audio_rtcp_channel = 0; // we get this information in the SETUP message reply
                            }
                            transport = new RtspTransport()
                            {
                                LowerTransport = RtspTransport.LowerTransportType.UDP,
                                IsMulticast    = true
                            };
                        }

                        // Generate SETUP messages
                        Rtsp.Messages.RtspRequestSetup setup_message = new Rtsp.Messages.RtspRequestSetup();
                        setup_message.RtspUri = new Uri(control);
                        setup_message.AddTransport(transport);
                        if (auth_type != null)
                        {
                            AddAuthorization(setup_message, username, password, auth_type, realm, nonce, url);
                        }

                        // Add SETUP message to list of mesages to send
                        setup_messages.Add(setup_message);
                    }
                }
                // Send the FIRST SETUP message and remove it from the list of Setup Messages
                rtsp_client.SendMessage(setup_messages[0]);
                setup_messages.RemoveAt(0);
            }


            // If we get a reply to SETUP (which was our third command), then we
            // (i) check if we have any more SETUP commands to send out (eg if we are doing SETUP for Video and Audio)
            // (ii) send a PLAY command if all the SETUP command have been sent
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestSetup)
            {
                // Got Reply to SETUP
                if (message.IsOk == false)
                {
                    Console.WriteLine("Got Error in SETUP Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                Console.WriteLine("Got reply from Setup. Session is " + message.Session);

                session = message.Session; // Session value used with Play, Pause, Teardown and and additional Setups

                // Check the Transport header
                if (message.Headers.ContainsKey(RtspHeaderNames.Transport))
                {
                    RtspTransport transport = RtspTransport.Parse(message.Headers[RtspHeaderNames.Transport]);

                    // Check if Transport header includes Multicast
                    if (transport.IsMulticast)
                    {
                        String multicast_address = transport.Destination;
                        video_data_channel = transport.Port.First;
                        video_rtcp_channel = transport.Port.Second;

                        // Create the Pair of UDP Sockets in Multicast mode
                        video_udp_pair = new Rtsp.UDPSocket(multicast_address, video_data_channel, multicast_address, video_rtcp_channel);
                        video_udp_pair.DataReceived += Rtp_DataReceived;
                        video_udp_pair.Start();

                        // TODO - Need to set audio_udp_pair
                    }
                }


                // Check if we have another SETUP command to send, then remote it from the list
                if (setup_messages.Count > 0)
                {
                    // send the next SETUP message, after adding in the 'session'
                    Rtsp.Messages.RtspRequestSetup next_setup = setup_messages[0];
                    next_setup.Session = session;
                    rtsp_client.SendMessage(next_setup);

                    setup_messages.RemoveAt(0);
                }

                else
                {
                    // Send PLAY
                    Rtsp.Messages.RtspRequest play_message = new Rtsp.Messages.RtspRequestPlay();
                    play_message.RtspUri = new Uri(url);
                    play_message.Session = session;
                    if (auth_type != null)
                    {
                        AddAuthorization(play_message, username, password, auth_type, realm, nonce, url);
                    }
                    rtsp_client.SendMessage(play_message);
                }
            }

            // If we get a reply to PLAY (which was our fourth command), then we should have video being received
            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestPlay)
            {
                // Got Reply to PLAY
                if (message.IsOk == false)
                {
                    Console.WriteLine("Got Error in PLAY Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                Console.WriteLine("Got reply from Play  " + message.Command);
            }
        }
Example #26
0
        /// <summary>
        /// Handles the response.
        /// </summary>
        /// <param name="message">A message.</param>
        private void HandleResponse(RtspResponse message)
        {
            Contract.Requires(message != null);

            if (message.OriginalRequest != null && message.OriginalRequest is RtspRequestSetup)
            {
                HandleResponseToSetup(message);
            }

            UpdateSessionState(message);

            //TODO rewrite instead of remove
            if (message.Headers.ContainsKey(RtspHeaderNames.ContentBase))
                message.Headers.Remove(RtspHeaderNames.ContentBase);

            if (message.Headers.ContainsKey(RtspHeaderNames.ContentType) &&
                message.Headers[RtspHeaderNames.ContentType] == "application/sdp")
            {
                RewriteSDPMessage(message);
            }
        }
Example #27
0
    // Process each RTSP message that is received
    private async System.Threading.Tasks.Task RTSP_Message_ReceivedAsync(object sender, RtspChunkEventArgs e)
    {
        // Cast the 'sender' and 'e' into the RTSP Listener (the Socket) and the RTSP Message
        Rtsp.RtspListener         listener = sender as Rtsp.RtspListener;
        Rtsp.Messages.RtspMessage message  = e.Message as Rtsp.Messages.RtspMessage;

        Console.WriteLine("RTSP message received " + message);
        var deviceId      = "";
        var streamId      = "";
        var unixTimestamp = 0;
        var startTime     = new DateTime(1970, 1, 1);

        if (message is RtspRequest)
        {
            var rtspParameters = HttpUtility.ParseQueryString(((RtspRequest)message).RtspUri.Query);
            deviceId = rtspParameters["deviceId"];
            streamId = rtspParameters["streamId"];

            int.TryParse(rtspParameters["unixTimestamp"], out unixTimestamp);
            startTime = startTime.AddSeconds(unixTimestamp);
            Console.WriteLine($"{rtspParameters["deviceId"]}, {rtspParameters["streamId"]},{rtspParameters["unixTimestamp"]}");
        }
        if (String.IsNullOrEmpty(deviceId))
        {
            _logger.Error("No deviceId");
            return;
        }
        List <RTSPConnection> rtsp_list = new List <RTSPConnection>();

        rtsp_list = _rtspList.GetOrAdd(deviceId, rtsp_list);
        // Check if the RTSP Message has valid authentication (validating against username,password,realm and nonce)
        bool           authorized = false;
        Authentication authInfo   = null;

        if (message.Headers.ContainsKey("Authorization") == true)
        {
            // The Header contained Authorization
            // Check the message has the correct Authorization
            // If it does not have the correct Authorization then close the RTSP connection
            authInfo = Authentication.GetAuthenticationInfo(message);
            URLCommand nvrCmd        = new URLCommand(_nvrIp, uint.Parse(_nvrPort), authInfo.Username, authInfo.Password);
            string     loginResponse = null;
            authorized = nvrCmd.Login(ref loginResponse);

            if (authorized == false)
            {
                // Send a 401 Authentication Failed reply, then close the RTSP Socket
                Rtsp.Messages.RtspResponse authorization_response = (e.Message as Rtsp.Messages.RtspRequest).CreateResponse();
                authorization_response.AddHeader("WWW-Authenticate: " + auth.GetHeader());
                authorization_response.ReturnCode = 401;
                listener.SendMessage(authorization_response);

                lock (rtsp_list)
                {
                    foreach (RTSPConnection connection in rtsp_list.ToArray())
                    {
                        if (connection.listener == listener)
                        {
                            rtsp_list.Remove(connection);
                        }
                    }
                }
                listener.Dispose();
                return;
            }
            else
            {
                lock (rtsp_list)
                {
                    if (!rtsp_list.Any(rtsp => rtsp.listener.RemoteAdress == listener.RemoteAdress))
                    {
                        RTSPConnection new_connection = new RTSPConnection();
                        new_connection.listener        = listener;
                        new_connection.client_hostname = listener.RemoteAdress.Split(':')[0];
                        new_connection.ssrc            = global_ssrc;
                        new_connection.time_since_last_rtsp_keepalive       = DateTime.UtcNow;
                        new_connection.video_time_since_last_rtcp_keepalive = DateTime.UtcNow;

                        rtsp_list.Add(new_connection);
                    }
                }
                _logger.Info($"Login NVR success:{loginResponse}");
            }
        }
        else
        {
            Rtsp.Messages.RtspResponse authorization_response = (e.Message as Rtsp.Messages.RtspRequest).CreateResponse();
            authorization_response.AddHeader("WWW-Authenticate: " + auth.GetHeader()); // 'Basic' or 'Digest'
            authorization_response.ReturnCode = 401;
            listener.SendMessage(authorization_response);
            return;
        }

        // Update the RTSP Keepalive Timeout
        // We could check that the message is GET_PARAMETER or OPTIONS for a keepalive but instead we will update the timer on any message
        lock (rtsp_list)
        {
            foreach (RTSPConnection connection in rtsp_list)
            {
                if (connection.listener.RemoteAdress.Equals(listener.RemoteAdress))
                {
                    // found the connection
                    connection.time_since_last_rtsp_keepalive = DateTime.UtcNow;
                    break;
                }
            }
        }


        // Handle OPTIONS message
        if (message is Rtsp.Messages.RtspRequestOptions)
        {
            // Create the reponse to OPTIONS
            Rtsp.Messages.RtspResponse options_response = (e.Message as Rtsp.Messages.RtspRequestOptions).CreateResponse();
            listener.SendMessage(options_response);
            // parse and get deviceId from url

            if (!_nvrPlayerList.ContainsKey(deviceId))
            {
                URLCommand nvrCmd          = new URLCommand(_nvrIp, uint.Parse(_nvrPort), authInfo.Username, authInfo.Password);
                string     deviceConfigXml = null;
                //DeviceConfig deviceConfig = null;
                //nvrCmd.GetDeviceConfig(ref deviceConfigXml, deviceId);

                //XmlDocument xdoc = new XmlDocument();
                try
                {
                    //xdoc.LoadXml(deviceConfigXml);
                    //XmlNodeReader reader = new XmlNodeReader(xdoc.DocumentElement);
                    //XmlSerializer ser = new XmlSerializer(typeof(DeviceConfig));
                    //deviceConfig = (DeviceConfig)ser.Deserialize(reader);
                    //var resolutionList = deviceConfig.Device.VideoQuality.Quality.Resolution1.Split('x');
                    //var widthStr = resolutionList[0].Replace("N", "");
                    //var heightStr = resolutionList[1];
                    //TODO get framerate instead of hardcode
                }
                catch (Exception ex)
                {
                    _logger.Error(ex.ToString());
                }
                //TODO open wmfplayer by session (device + stream or device + stream + IP&Port + playback time)
                WmfPlayer wmfPlayer = new WmfPlayer(new IntPtr(Int32.Parse(deviceId)));
                wmfPlayer.m_SelectID = deviceId;
                if (_nvrPlayerList.TryAdd(deviceId, wmfPlayer))
                {
                    wmfPlayer.ReceivedYUVFrame += video_source_ReceivedYUVFrame;
                    await wmfPlayer.OpenVideoAsync(_nvrIp, uint.Parse(_nvrPort), authInfo.Username, authInfo.Password, startTime, 1, deviceId, int.Parse(streamId));
                }
            }
        }
        else if (message is Rtsp.Messages.RtspRequestDescribe) // Handle DESCRIBE message
        {
            String requested_url = (message as Rtsp.Messages.RtspRequestDescribe).RtspUri.ToString();
            Console.WriteLine("Request for " + requested_url);

            // TODO. Check the requsted_url is valid. In this example we accept any RTSP URL
            // Make the Base64 SPS and PPS
            raw_sps = h264_encoder.GetRawSPS(); // no 0x00 0x00 0x00 0x01 or 32 bit size header
            raw_pps = h264_encoder.GetRawPPS(); // no 0x00 0x00 0x00 0x01 or 32 bit size header
            String sps_str = Convert.ToBase64String(raw_sps);
            String pps_str = Convert.ToBase64String(raw_pps);

            StringBuilder sdp = new StringBuilder();

            // Generate the SDP
            // The sprop-parameter-sets provide the SPS and PPS for H264 video
            // The packetization-mode defines the H264 over RTP payloads used but is Optional
            sdp.Append("v=0\n");
            sdp.Append($"o={authInfo.Username} 0 0 IN IP4 0.0.0.0\n");
            sdp.Append("s=ACTi NVR\n");
            sdp.Append("m=video 0 RTP/AVP 96\n");
            sdp.Append("c=IN IP4 0.0.0.0\n");
            sdp.Append("a=control:*\n");
            sdp.Append("a=rtpmap:96 H264/90000\n");
            sdp.Append("a=fmtp:96 packetization-mode=1;profile-level-id=4D6028; sprop-parameter-sets=" + sps_str + "," + pps_str + ";\n");

            byte[] sdp_bytes = Encoding.ASCII.GetBytes(sdp.ToString());

            // Create the reponse to DESCRIBE
            // This must include the Session Description Protocol (SDP)
            Rtsp.Messages.RtspResponse describe_response = (e.Message as Rtsp.Messages.RtspRequestDescribe).CreateResponse();

            describe_response.AddHeader("Content-Base: " + requested_url);
            describe_response.AddHeader("Content-Type: application/sdp");
            describe_response.Data = sdp_bytes;
            describe_response.AdjustContentLength();
            listener.SendMessage(describe_response);
        }
        else if (message is Rtsp.Messages.RtspRequestSetup)// Handle SETUP message
        {
            //
            var setupMessage = message as Rtsp.Messages.RtspRequestSetup;

            // Check the RTSP transport
            // If it is UDP or Multicast, create the sockets
            // If it is RTP over RTSP we send data via the RTSP Listener

            // FIXME client may send more than one possible transport.
            // very rare
            Rtsp.Messages.RtspTransport transport = setupMessage.GetTransports()[0];


            // Construct the Transport: reply from the Server to the client
            Rtsp.Messages.RtspTransport transport_reply = new Rtsp.Messages.RtspTransport();
            transport_reply.SSrc = global_ssrc.ToString("X8"); // Convert to Hex, padded to 8 characters

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP)
            {
                // RTP over RTSP mode}
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.TCP;
                transport_reply.Interleaved    = new Rtsp.Messages.PortCouple(transport.Interleaved.First, transport.Interleaved.Second);
            }

            Rtsp.UDPSocket udp_pair = null;
            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP &&
                transport.IsMulticast == false)
            {
                Boolean udp_supported = true;
                if (udp_supported)
                {
                    // RTP over UDP mode
                    // Create a pair of UDP sockets - One is for the Video, one is for the RTCP
                    udp_pair = new Rtsp.UDPSocket(50000, 51000); // give a range of 500 pairs (1000 addresses) to try incase some address are in use
                    udp_pair.DataReceived += (object local_sender, RtspChunkEventArgs local_e) => {
                        // RTCP data received
                        Console.WriteLine("RTCP data received " + local_sender.ToString() + " " + local_e.ToString());
                    };
                    udp_pair.Start(); // start listening for data on the UDP ports

                    // Pass the Port of the two sockets back in the reply
                    transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.UDP;
                    transport_reply.IsMulticast    = false;
                    transport_reply.ClientPort     = new Rtsp.Messages.PortCouple(udp_pair.data_port, udp_pair.control_port);
                }
                else
                {
                    transport_reply = null;
                }
            }

            if (transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP &&
                transport.IsMulticast == true)
            {
                // RTP over Multicast UDP mode}
                // Create a pair of UDP sockets in Multicast Mode
                // Pass the Ports of the two sockets back in the reply
                transport_reply.LowerTransport = Rtsp.Messages.RtspTransport.LowerTransportType.UDP;
                transport_reply.IsMulticast    = true;
                transport_reply.Port           = new Rtsp.Messages.PortCouple(7000, 7001); // FIX

                // for now until implemented
                transport_reply = null;
            }


            if (transport_reply != null)
            {
                // Update the session with transport information
                String copy_of_session_id = "";
                lock (rtsp_list)
                {
                    foreach (RTSPConnection connection in rtsp_list)
                    {
                        if (connection.listener.RemoteAdress.Equals(listener.RemoteAdress))
                        {
                            // ToDo - Check the Track ID to determine if this is a SETUP for the Video Stream
                            // or a SETUP for an Audio Stream.
                            // In the SDP the H264 video track is TrackID 0


                            // found the connection
                            // Add the transports to the connection
                            connection.video_client_transport = transport;
                            connection.video_transport_reply  = transport_reply;

                            // If we are sending in UDP mode, add the UDP Socket pair and the Client Hostname
                            connection.video_udp_pair = udp_pair;


                            connection.video_session_id = session_handle.ToString();
                            session_handle++;


                            // Copy the Session ID
                            copy_of_session_id = connection.video_session_id;
                            break;
                        }
                    }
                }

                Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse();
                setup_response.Headers[Rtsp.Messages.RtspHeaderNames.Transport] = transport_reply.ToString();
                setup_response.Session = copy_of_session_id;
                listener.SendMessage(setup_response);
            }
            else
            {
                Rtsp.Messages.RtspResponse setup_response = setupMessage.CreateResponse();
                // unsuported transport
                setup_response.ReturnCode = 461;
                listener.SendMessage(setup_response);
            }
        }
        else if (message is Rtsp.Messages.RtspRequestPlay)// Handle PLAY message (Sent with a Session ID)
        {
            lock (rtsp_list)
            {
                // Search for the Session in the Sessions List. Change the state to "PLAY"
                bool session_found = false;
                foreach (RTSPConnection connection in rtsp_list)
                {
                    if (message.Session == connection.video_session_id) /* OR AUDIO_SESSION_ID */
                    {
                        // found the session
                        session_found   = true;
                        connection.play = true;                                                                                                   // ACTUALLY YOU COULD PAUSE JUST THE VIDEO (or JUST THE AUDIO)

                        string range    = "npt=0-";                                                                                               // Playing the 'video' from 0 seconds until the end
                        string rtp_info = "url=" + ((Rtsp.Messages.RtspRequestPlay)message).RtspUri + ";seq=" + connection.video_sequence_number; // TODO Add rtptime  +";rtptime="+session.rtp_initial_timestamp;

                        // Send the reply
                        Rtsp.Messages.RtspResponse play_response = (e.Message as Rtsp.Messages.RtspRequestPlay).CreateResponse();
                        play_response.AddHeader("Range: " + range);
                        play_response.AddHeader("RTP-Info: " + rtp_info);
                        listener.SendMessage(play_response);

                        break;
                    }
                }

                if (session_found == false)
                {
                    // Session ID was not found in the list of Sessions. Send a 454 error
                    Rtsp.Messages.RtspResponse play_failed_response = (e.Message as Rtsp.Messages.RtspRequestPlay).CreateResponse();
                    play_failed_response.ReturnCode = 454; // Session Not Found
                    listener.SendMessage(play_failed_response);
                }
            }
        }
        else if (message is Rtsp.Messages.RtspRequestPause) // Handle PAUSE message (Sent with a Session ID)
        {
            lock (rtsp_list)
            {
                // Search for the Session in the Sessions List. Change the state of "PLAY"
                foreach (RTSPConnection connection in rtsp_list)
                {
                    if (message.Session == connection.video_session_id /* OR AUDIO SESSION ID */)
                    {
                        // found the session
                        connection.play = false; // COULD HAVE PLAY/PAUSE FOR VIDEO AND AUDIO
                        break;
                    }
                }
            }

            // ToDo - only send back the OK response if the Session in the RTSP message was found
            Rtsp.Messages.RtspResponse pause_response = (e.Message as Rtsp.Messages.RtspRequestPause).CreateResponse();
            listener.SendMessage(pause_response);
        }


        // Handle GET_PARAMETER message, often used as a Keep Alive
        if (message is Rtsp.Messages.RtspRequestGetParameter)
        {
            // Create the reponse to GET_PARAMETER
            Rtsp.Messages.RtspResponse getparameter_response = (e.Message as Rtsp.Messages.RtspRequestGetParameter).CreateResponse();
            listener.SendMessage(getparameter_response);
        }


        // Handle TEARDOWN (sent with a Session ID)
        if (message is Rtsp.Messages.RtspRequestTeardown)
        {
            lock (rtsp_list)
            {
                // Search for the Session in the Sessions List.
                foreach (RTSPConnection connection in rtsp_list.ToArray()) // Convert to ToArray so we can delete from the rtp_list
                {
                    if (message.Session == connection.video_session_id)    // SHOULD HAVE AN AUDIO TEARDOWN AS WELL
                    {
                        // If this is UDP, close the transport
                        // For TCP there is no transport to close (as RTP packets were interleaved into the RTSP connection)
                        if (connection.video_udp_pair != null)
                        {
                            connection.video_udp_pair.Stop();
                            connection.video_udp_pair = null;
                        }

                        rtsp_list.Remove(connection);

                        // Close the RTSP socket
                        listener.Dispose();
                    }
                }
            }
        }
    }
Example #28
0
        /// <summary>
        /// Updates the state of the session.
        /// </summary>
        /// <param name="message">A response message.</param>
        private void UpdateSessionState(RtspResponse message)
        {
            // if no session can be found
            if (message.OriginalRequest == null ||
                message.Session == null ||
                message.OriginalRequest.RtspUri == null)
                return;

            //Update session state and handle special message
            string sessionKey = RtspSession.GetSessionName(message.OriginalRequest.RtspUri, message.Session);
            if (_activesSession.ContainsKey(sessionKey))
            {
                if (message.ReturnCode >= 300 && message.ReturnCode < 400)
                    _activesSession[sessionKey].State = RtspSession.SessionState.Init;
                else if (message.ReturnCode < 300)
                {
                    switch (message.OriginalRequest.RequestTyped)
                    {
                        case RtspRequest.RequestType.SETUP:
                            if (_activesSession[sessionKey].State == RtspSession.SessionState.Init)
                                _activesSession[sessionKey].State = RtspSession.SessionState.Ready;
                            break;
                        case RtspRequest.RequestType.PLAY:
                            if (_activesSession[sessionKey].State == RtspSession.SessionState.Ready)
                                _activesSession[sessionKey].State = RtspSession.SessionState.Playing;
                            break;
                        case RtspRequest.RequestType.RECORD:
                            if (_activesSession[sessionKey].State == RtspSession.SessionState.Ready)
                                _activesSession[sessionKey].State = RtspSession.SessionState.Recording;
                            break;
                        case RtspRequest.RequestType.PAUSE:
                            if (_activesSession[sessionKey].State == RtspSession.SessionState.Playing ||
                                _activesSession[sessionKey].State == RtspSession.SessionState.Recording)
                                _activesSession[sessionKey].State = RtspSession.SessionState.Ready;
                            break;
                        case RtspRequest.RequestType.TEARDOWN:
                            _activesSession[sessionKey].State = RtspSession.SessionState.Init;

                            break;
                    }
                }
            }
            else
            {
                _logger.Warn("Command {0} for session {1} which was not found", message.OriginalRequest.RequestTyped, sessionKey);
            }
        }
Example #29
0
        /// <summary>
        /// Gets the assiociate OK response with the request.
        /// </summary>
        /// <returns>an Rtsp response correcponding to request.</returns>
        public virtual RtspResponse CreateResponse()
        {
            RtspResponse returnValue = new RtspResponse();
            returnValue.ReturnCode = 200;
            returnValue.CSeq = this.CSeq;
            if (this.Headers.ContainsKey(RtspHeaderNames.Session))
            {
                returnValue.Headers[RtspHeaderNames.Session] = this.Headers[RtspHeaderNames.Session];
            }

            return returnValue;
        }