예제 #1
0
        internal RtspResponse HandlePullSetup(RtspRequestSetup request)
        {
            var response = request.CreateResponse();

            if (string.IsNullOrEmpty(response.Session))
            {
                // TODO Allocate a real session ID
                response.Session = sessionGenerator.Next().ToString();
            }

            var pushUri = GetPushUri(request.RtspUri.AbsolutePath);

            RtspPushDescription description;

            if (PushDescriptions.TryGetValue(pushUri, out description))
            {
                //TODO get port and multicast address from description.
                var forwarder = description.GetForwarderFor(pushUri);
                var transport = new RtspTransport();

                RtspTransport newTransport = new RtspTransport()
                {
                    IsMulticast = true,
                    Destination = forwarder.ForwardHostVideo,
                    Port        = new PortCouple(forwarder.ForwardPortVideo, forwarder.ListenCommandPort)
                };
                response.Headers[RtspHeaderNames.Transport] = newTransport.ToString();
            }
            else
            {
                response.ReturnCode = 404;
            }
            return(response);
        }
예제 #2
0
        public void ToStringTCP()
        {
            RtspTransport transport = new RtspTransport()
            {
                LowerTransport = RtspTransport.LowerTransportType.TCP,
                Interleaved    = new PortCouple(0, 1),
            };

            Assert.AreEqual("RTP/AVP/TCP;interleaved=0-1", transport.ToString());
        }
예제 #3
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;
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Configures the transport and forwarder.
        /// </summary>
        /// <param name="aMessage">A message.</param>
        /// <param name="forwarder">The preset forwarder.</param>
        /// <returns>The configured forwarder.</returns>
        private static Forwarder ConfigureTransportAndForwarder(RtspMessage aMessage, UDPForwarder forwarder)
        {
            RtspTransport transport = RtspTransport.Parse(aMessage.Headers[RtspHeaderNames.Transport]);

            Forwarder resultForwarder;

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

                resultForwarder = TCPForwarder;
            }

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

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

            return(resultForwarder);
        }
예제 #5
0
        /// <summary>
        /// Handles a request setup.
        /// </summary>
        /// <param name="destination">The destination.</param>
        /// <param name="requestSetup">The request setup.</param>
        /// <returns>The rewritten message</returns>
        /// <remarks>
        /// The destination can be modified.
        /// </remarks>
        private RtspMessage HandleRequestSetup(ref RtspListener destination, RtspRequestSetup requestSetup)
        {
            Contract.Requires(requestSetup != null);
            Contract.Requires(destination != null);
            Contract.Ensures(Contract.Result <RtspMessage>() != null);
            Contract.Ensures(Contract.ValueAtReturn(out destination) != null);


            // look if we already have a multicast streaming playing for this URI.
            foreach (var session in _activesSession.Values)
            {
                if (session.State == RtspSession.SessionState.Playing && session.ListOfForwader.ContainsKey(requestSetup.RtspUri))
                {
                    Forwarder existingForwarder = session.ListOfForwader[requestSetup.RtspUri];
                    if (existingForwarder != null && existingForwarder.ToMulticast)
                    {
                        RtspResponse returnValue = requestSetup.CreateResponse();
                        returnValue.Headers[RtspHeaderNames.Transport] = new RtspTransport()
                        {
                            IsMulticast = true,
                            Destination = existingForwarder.ForwardHostVideo,
                            Port        = new PortCouple(existingForwarder.ForwardPortVideo, existingForwarder.ListenCommandPort),
                        }.ToString();
                        returnValue.Session = session.Name;
                        destination         = requestSetup.SourcePort;
                        return(returnValue);
                    }
                }
            }


            string setupKey = requestSetup.SourcePort.RemoteAdress + "SEQ" + requestSetup.CSeq.ToString(CultureInfo.InvariantCulture);

            RtspTransport selectedTransport = SelectTransport(requestSetup);

            // We do not handle asked transport so return directly.
            if (selectedTransport == null)
            {
                _logger.Info("No transport asked are supported, sorry");
                RtspResponse returnValue = requestSetup.CreateResponse();
                // Unsupported transport;
                returnValue.ReturnCode = 461;
                destination            = requestSetup.SourcePort;
                return(returnValue);
            }

            UDPForwarder forwarder = new UDPForwarder();

            forwarder.ToMulticast = selectedTransport.IsMulticast;

            // this part of config is only valid in unicast.
            if (!selectedTransport.IsMulticast)
            {
                forwarder.ForwardPortVideo  = selectedTransport.ClientPort.First;
                forwarder.SourcePortCommand = selectedTransport.ClientPort.Second;

                // If the client did not set the destination.. get it from TCP source
                if (!string.IsNullOrEmpty(selectedTransport.Destination))
                {
                    forwarder.ForwardHostVideo = selectedTransport.Destination;
                }
                else
                {
                    forwarder.ForwardHostVideo = requestSetup.SourcePort.RemoteAdress.Split(':')[0];
                    _logger.Debug("Destination get from TCP port {0}", forwarder.ForwardHostVideo);
                }
            }

            // Configured the transport asked.
            forwarder.ForwardHostCommand = destination.RemoteAdress.Split(':')[0];
            RtspTransport firstNewTransport = new RtspTransport()
            {
                IsMulticast = false,
                ClientPort  = new PortCouple(forwarder.ListenVideoPort, forwarder.FromForwardCommandPort),
            };

            RtspTransport secondTransport = new RtspTransport()
            {
                IsMulticast    = false,
                LowerTransport = RtspTransport.LowerTransportType.TCP,
            };

            requestSetup.Headers[RtspHeaderNames.Transport] = firstNewTransport.ToString() + ", " + secondTransport.ToString();
            requestSetup.Headers[RtspHeaderNames.Transport] = firstNewTransport.ToString();// +", " + secondTransport.ToString();
            _setupForwarder.Add(setupKey, forwarder);

            return(requestSetup);
        }
예제 #6
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
            var listener = sender as RtspListener;
            var message  = e.Message as RtspMessage;

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

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

            // Handle DESCRIBE message
            if (message is RtspRequestDescribe)
            {
                var requested_url = (message as 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
                var raw_sps = h264_encoder.GetRawSPS();                 // no 0x00 0x00 0x00 0x01 or 32 bit size header
                var raw_pps = h264_encoder.GetRawPPS();                 // no 0x00 0x00 0x00 0x01 or 32 bit size header
                var sps_str = Convert.ToBase64String(raw_sps);
                var pps_str = Convert.ToBase64String(raw_pps);

                var 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");

                var sdp_bytes = Encoding.ASCII.GetBytes(sdp.ToString());

                // Create the reponse to DESCRIBE
                // This must include the Session Description Protocol (SDP)
                var describe_response = (e.Message as 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 RtspRequestSetup)
            {
                //
                var setupMessage = message as 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
                var transport = setupMessage.GetTransports()[0];

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

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

                if (transport.LowerTransport == 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 = RtspTransport.LowerTransportType.UDP;
                    transport_reply.IsMulticast    = false;
                    transport_reply.ClientPort     = transport.ClientPort;                 // FIX

                    // for now until implemented
                    transport_reply = null;
                }

                if (transport.LowerTransport == RtspTransport.LowerTransportType.UDP &&
                    transport.IsMulticast)
                {
                    // 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 = RtspTransport.LowerTransportType.UDP;
                    transport_reply.IsMulticast    = true;
                    transport_reply.Port           = new PortCouple(7000, 7001);           // FIX

                    // for now until implemented
                    transport_reply = null;
                }

                if (transport_reply != null)
                {
                    var 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++;
                    }

                    var setup_response = setupMessage.CreateResponse();
                    setup_response.Headers[RtspHeaderNames.Transport] = transport_reply.ToString();
                    setup_response.Session = new_session.session_id;
                    listener.SendMessage(setup_response);
                }
                else
                {
                    var setup_response = setupMessage.CreateResponse();

                    // unsuported transport
                    setup_response.ReturnCode = 461;
                    listener.SendMessage(setup_response);
                }
            }

            // Handle PLAY message
            if (message is RtspRequestPlay)
            {
                lock (rtp_list)
                {
                    // Search for the Session in the Sessions List. Change the state of "PLAY"
                    foreach (var 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
                var play_response = (e.Message as RtspRequestPlay).CreateResponse();
                listener.SendMessage(play_response);
            }

            // Handle PLAUSE message
            if (message is RtspRequestPause)
            {
                lock (rtp_list)
                {
                    // Search for the Session in the Sessions List. Change the state of "PLAY"
                    foreach (var 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
                var pause_response = (e.Message as RtspRequestPause).CreateResponse();
                listener.SendMessage(pause_response);
            }

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

            // Handle TEARDOWN
            if (message is RtspRequestTeardown)
            {
                lock (rtp_list)
                {
                    // Search for the Session in the Sessions List.
                    foreach (var 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();
                        }
                    }
                }
            }
        }
예제 #7
0
        public void ToStringUDPUnicast()
        {
            RtspTransport transport = new RtspTransport()
            {
                LowerTransport = RtspTransport.LowerTransportType.UDP,
                IsMulticast    = false,
                ClientPort     = new PortCouple(5000, 5001),
                ServerPort     = new PortCouple(5002, 5003),
                Destination    = "1.2.3.4"
            };

            Assert.AreEqual("RTP/AVP/UDP;unicast;destination=1.2.3.4;client_port=5000-5001;server_port=5002-5003", transport.ToString());
        }