Example #1
0
        private static void ReadTransport(RtspTransport returnValue, string[] transportProtocolPart)
        {
            TransportType transport;

            if (!Enum.TryParse <TransportType>(transportProtocolPart[0], out transport))
            {
                throw new ArgumentException("Transport type invalid", "aTransportString");
            }
            returnValue.Transport = transport;
        }
Example #2
0
        private static void ReadProfile(RtspTransport returnValue, string[] transportProtocolPart)
        {
            ProfileType profile;

            if (transportProtocolPart.Length < 2 || !Enum.TryParse <ProfileType>(transportProtocolPart[1], out profile))
            {
                throw new ArgumentException("Transport profile type invalid", "aTransportString");
            }
            returnValue.Profile = profile;
        }
Example #3
0
        public void AddTransport(RtspTransport newTransport)
        {
            string actualTransport = string.Empty;

            if (Headers.ContainsKey(RtspHeaderNames.Transport))
            {
                actualTransport = Headers[RtspHeaderNames.Transport] + ",";
            }
            Headers[RtspHeaderNames.Transport] = actualTransport + newTransport.ToString();
        }
Example #4
0
 private static void ReadLowerTransport(RtspTransport returnValue, string[] transportProtocolPart)
 {
     if (transportProtocolPart.Length == 3)
     {
         LowerTransportType lowerTransport;
         if (!Enum.TryParse <LowerTransportType>(transportProtocolPart[2], out lowerTransport))
         {
             throw new ArgumentException("Lower transport type invalid", "aTransportString");
         }
         returnValue.LowerTransport = lowerTransport;
     }
 }
Example #5
0
        /// <summary>
        /// Parses the specified transport string.
        /// </summary>
        /// <param name="aTransportString">A transport string.</param>
        /// <returns>The transport class.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="aTransportString"/> is null.</exception>
        public static RtspTransport Parse(string aTransportString)
        {
            if (aTransportString == null)
            {
                throw new ArgumentNullException("aTransportString");
            }
            Contract.EndContractBlock();

            RtspTransport returnValue = new RtspTransport();

            string[] transportPart         = aTransportString.Split(';');
            string[] transportProtocolPart = transportPart[0].Split('/');

            ReadTransport(returnValue, transportProtocolPart);
            ReadProfile(returnValue, transportProtocolPart);
            ReadLowerTransport(returnValue, transportProtocolPart);

            foreach (string part in transportPart)
            {
                string[] subPart = part.Split('=');

                switch (subPart[0].ToUpperInvariant())
                {
                case "UNICAST":
                    returnValue.IsMulticast = false;
                    break;

                case "MULTICAST":
                    returnValue.IsMulticast = true;
                    break;

                case "DESTINATION":
                    if (subPart.Length == 2)
                    {
                        returnValue.Destination = subPart[1];
                    }
                    break;

                case "SOURCE":
                    if (subPart.Length == 2)
                    {
                        returnValue.Source = subPart[1];
                    }
                    break;

                case "INTERLEAVED":
                    returnValue.IsMulticast = false;
                    if (subPart.Length < 2)
                    {
                        throw new ArgumentException("interleaved value invalid", "aTransportString");
                    }

                    returnValue.Interleaved = PortCouple.Parse(subPart[1]);
                    break;

                case "APPEND":
                    returnValue.IsAppend = true;
                    break;

                case "TTL":
                    int ttl = 0;
                    if (subPart.Length < 2 || !int.TryParse(subPart[1], out ttl))
                    {
                        throw new ArgumentException("TTL value invalid", "aTransportString");
                    }
                    returnValue.TTL = ttl;
                    break;

                case "LAYERS":
                    int layers = 0;
                    if (subPart.Length < 2 || !int.TryParse(subPart[1], out layers))
                    {
                        throw new ArgumentException("Layers value invalid", "aTransportString");
                    }
                    returnValue.TTL = layers;
                    break;

                case "PORT":
                    if (subPart.Length < 2)
                    {
                        throw new ArgumentException("Port value invalid", "aTransportString");
                    }
                    returnValue.Port = PortCouple.Parse(subPart[1]);
                    break;

                case "CLIENT_PORT":
                    if (subPart.Length < 2)
                    {
                        throw new ArgumentException("client_port value invalid", "aTransportString");
                    }
                    returnValue.ClientPort = PortCouple.Parse(subPart[1]);
                    break;

                case "SERVER_PORT":
                    if (subPart.Length < 2)
                    {
                        throw new ArgumentException("server_port value invalid", "aTransportString");
                    }
                    returnValue.ServerPort = PortCouple.Parse(subPart[1]);
                    break;

                case "SSRC":
                    if (subPart.Length < 2)
                    {
                        throw new ArgumentException("ssrc value invalid", "aTransportString");
                    }
                    returnValue.SSrc = subPart[1];
                    break;

                case "MODE":
                    if (subPart.Length < 2)
                    {
                        throw new ArgumentException("mode value invalid", "aTransportString");
                    }
                    returnValue.Mode = subPart[1];
                    break;

                default:
                    // TODO log invalid part
                    break;
                }
            }
            return(returnValue);
        }
Example #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
            hololabCamera.RtspListener         listener = sender as hololabCamera.RtspListener;
            hololabCamera.Messages.RtspMessage message  = e.Message as hololabCamera.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)
                {
                    // 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
                        hololabCamera.Messages.RtspResponse authorization_response = (e.Message as hololabCamera.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;
                    }
                }
                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
                    hololabCamera.Messages.RtspResponse authorization_response = (e.Message as hololabCamera.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 hololabCamera.Messages.RtspRequestOptions)
            {
                // Create the reponse to OPTIONS
                hololabCamera.Messages.RtspResponse options_response = (e.Message as hololabCamera.Messages.RtspRequestOptions).CreateResponse();
                listener.SendMessage(options_response);
            }

            // Handle DESCRIBE message
            if (message is hololabCamera.Messages.RtspRequestDescribe)
            {
                String requested_url = (message as hololabCamera.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=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)
                hololabCamera.Messages.RtspResponse describe_response = (e.Message as hololabCamera.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 hololabCamera.Messages.RtspRequestSetup)
            {
                //
                var setupMessage = message as hololabCamera.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
                hololabCamera.Messages.RtspTransport transport = setupMessage.GetTransports()[0];


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

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

                hololabCamera.UDPSocket udp_pair = null;
                if (transport.LowerTransport == hololabCamera.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 hololabCamera.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 = hololabCamera.Messages.RtspTransport.LowerTransportType.UDP;
                        transport_reply.IsMulticast    = false;
                        transport_reply.ClientPort     = new hololabCamera.Messages.PortCouple(udp_pair.data_port, udp_pair.control_port);
                    }
                    else
                    {
                        transport_reply = null;
                    }
                }

                if (transport.LowerTransport == hololabCamera.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 = hololabCamera.Messages.RtspTransport.LowerTransportType.UDP;
                    transport_reply.IsMulticast    = true;
                    transport_reply.Port           = new hololabCamera.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;
                            }
                        }
                    }

                    hololabCamera.Messages.RtspResponse setup_response = setupMessage.CreateResponse();
                    setup_response.Headers[hololabCamera.Messages.RtspHeaderNames.Transport] = transport_reply.ToString();
                    setup_response.Session = copy_of_session_id;
                    listener.SendMessage(setup_response);
                }
                else
                {
                    hololabCamera.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 hololabCamera.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=" + ((hololabCamera.Messages.RtspRequestPlay)message).RtspUri + ";seq=" + connection.video_sequence_number; // TODO Add rtptime  +";rtptime="+session.rtp_initial_timestamp;

                            // Send the reply
                            hololabCamera.Messages.RtspResponse play_response = (e.Message as hololabCamera.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
                        hololabCamera.Messages.RtspResponse play_failed_response = (e.Message as hololabCamera.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 hololabCamera.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
                hololabCamera.Messages.RtspResponse pause_response = (e.Message as hololabCamera.Messages.RtspRequestPause).CreateResponse();
                listener.SendMessage(pause_response);
            }


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


            // Handle TEARDOWN (sent with a Session ID)
            if (message is hololabCamera.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();
                        }
                    }
                }
            }
        }