示例#1
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);

            // 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;
                    }
                }
            }

            #region 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("DESCRIBE,SETUP,PLAY,TEARDOWN");
                listener.SendMessage(options_response);
            }
            #endregion

            #region 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
                String sps_str = Convert.ToBase64String(StreamInfo.SPS);
                String pps_str = Convert.ToBase64String(StreamInfo.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=SysDVR - https://github.com/exelix11/sysdvr - [PID {Process.GetCurrentProcess().Id}]\n");
                if (video_source != null)
                {
                    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");
                }
                if (audio_source != null)
                {
                    sdp.Append("m=audio 0 RTP/AVP 97\n");
                    sdp.Append("a=rtpmap:97 L16/48000/2\n");
                    sdp.Append("a=control:trackID=1\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);
            }
            #endregion

            #region 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))
                            {
                                var stream = int.Parse(setupMessage.RtspUri.LocalPath.Split('=')[1]);
                                connection.sessions[stream].is_active = true;
                                // found the connection
                                // Add the transports to the connection
                                connection.sessions[stream].client_transport = transport;
                                connection.sessions[stream].transport_reply  = transport_reply;

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

                                connection.sessions[stream].session_id = session_handle.ToString();
                                session_handle++;

                                // Copy the Session ID
                                copy_of_session_id = connection.sessions[stream].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);
                }
            }
            #endregion

            #region Handle PLAY message
            // Must have 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 || message.Session == connection.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);
                    }
                }
            }
            #endregion

            #region Handle TEARDOWN
            // Must have 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
                    {
                        rtsp_list.Remove(connection);
                        connection.Dispose();
                        listener.Dispose();
                    }
                }
            }
            #endregion
        }
示例#2
0
        private void Rtsp_client_MessageReceived(object sender, Rtsp.RtspChunkEventArgs e)
        {
            Console.WriteLine("Message Received " + e.ToString());

            Rtsp.Messages.RtspResponse message = e.Message as Rtsp.Messages.RtspResponse;

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

            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestOptions)
            {
                // send the Describe
                Rtsp.Messages.RtspRequest describe_message = new Rtsp.Messages.RtspRequestDescribe();
                describe_message.RtspUri = new Uri(url);
                rtsp_client.SendMessage(describe_message);
            }

            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.
                // If the attribute is for Video, then carry out a SETUP and a PLAY

                for (int x = 0; x < sdp_data.Medias.Count; x++)
                {
                    if (sdp_data.Medias[x].GetMediaType() == Rtsp.Sdp.Media.MediaType.video)
                    {
                        // seach the atributes for control, fmtp and rtpmap
                        String control = ""; // the "track" or "stream id"
                        String fmtp    = ""; // holds SPS and PPS
                        String rtpmap  = ""; // 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.Value;
                            }
                            if (attrib.Key.Equals("rtpmap"))
                            {
                                rtpmap = attrib.Value;
                            }
                        }

                        String[] split_rtpmap = rtpmap.Split(' ');
                        video_payload = 0;
                        bool result = Int32.TryParse(split_rtpmap[0], out video_payload);


                        // Transport: RTP/AVP;unicast;client_port=8000-8001
                        // Transport: RTP/AVP/TCP;interleaved=0-1

                        Rtsp.Messages.RtspRequest setup_message = new Rtsp.Messages.RtspRequestSetup();
                        setup_message.RtspUri = new Uri(url + "/" + control);
                        setup_message.AddHeader("Transport: RTP/AVP/TCP;interleaved=0");
                        rtsp_client.SendMessage(setup_message);
                    }
                }
            }

            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestSetup)
            {
                // Got Reply to SETUP
                Console.WriteLine("Got reply from Setup. Session is " + message.Session);

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

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

            if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestPlay)
            {
                // Got Reply to PLAU
                Console.WriteLine("Got reply from Play  " + message.Command);
            }
        }