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);
        }
        /// <summary>
        /// Selects the transport based on the configuration of the system..
        /// </summary>
        /// <param name="requestSetup">The request setup message.</param>
        /// <returns>The selected transport</returns>
        /// <remarks>
        /// The transport is selected by taking the first supported transport
        /// in order of appearence.
        /// </remarks>
        private static RtspTransport SelectTransport(RtspRequestSetup requestSetup)
        {
            RtspTransport selectedTransport  = null;
            const bool    acceptTCP          = false;
            const bool    acceptUDPUnicast   = true;
            const bool    acceptUDPMulticast = true;

            foreach (RtspTransport proposedTransport in requestSetup.GetTransports())
            {
                if (acceptTCP && proposedTransport.LowerTransport == RtspTransport.LowerTransportType.TCP)
                {
                    selectedTransport = proposedTransport;
                    break;
                }
                if (acceptUDPMulticast && proposedTransport.LowerTransport == RtspTransport.LowerTransportType.UDP &&
                    proposedTransport.IsMulticast)
                {
                    selectedTransport = proposedTransport;
                    break;
                }
                if (acceptUDPUnicast && proposedTransport.LowerTransport == RtspTransport.LowerTransportType.UDP &&
                    !proposedTransport.IsMulticast)
                {
                    selectedTransport = proposedTransport;
                    break;
                }
            }
            return(selectedTransport);
        }
 public void DefaultValue()
 {
     RtspTransport testValue = new RtspTransport();
     Assert.IsTrue(testValue.IsMulticast);
     Assert.AreEqual(RtspTransport.LowerTransportType.UDP, testValue.LowerTransport);
     Assert.AreEqual("PLAY", testValue.Mode);
 }
Beispiel #4
0
        private void ProcessSetupRequest(RtspResponse message)
        {
            // If we get a reply to SETUP (which was our third command), then process then send PLAY

            // Got Reply to SETUP
            Logger.Info("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 multicastAddress = transport.Destination;
                    videoDataChannel = transport.Port.First;
                    videoRTCPChannel = transport.Port.Second;

                    // Create the Pair of UDP Sockets in Multicast mode
                    udpPair = new UDPSocketPair(multicastAddress, videoDataChannel, multicastAddress, videoRTCPChannel);
                    udpPair.DataReceived += RtpDataReceived;
                    udpPair.Start();
                }
            }

            RtspRequest play_message = new RtspRequestPlay
            {
                RtspUri = new Uri(url),
                Session = session
            };

            rtspListener.SendMessage(play_message);
        }
        public void DefaultValue()
        {
            RtspTransport testValue = new RtspTransport();

            Assert.IsTrue(testValue.IsMulticast);
            Assert.AreEqual(RtspTransport.LowerTransportType.UDP, testValue.LowerTransport);
            Assert.AreEqual("PLAY", testValue.Mode);
        }
        public void Parse1()
        {
            RtspTransport testValue = RtspTransport.Parse("RTP/AVP/UDP;destination");

            Assert.IsTrue(testValue.IsMulticast);
            Assert.AreEqual(RtspTransport.LowerTransportType.UDP, testValue.LowerTransport);
            Assert.AreEqual("PLAY", testValue.Mode);
        }
Beispiel #7
0
        public void Parse3()
        {
            RtspTransport testValue = RtspTransport.Parse("RTP/AVP/TCP;interleaved=3-4");

            Assert.IsFalse(testValue.IsMulticast);
            Assert.AreEqual(RtspTransport.LowerTransportType.TCP, testValue.LowerTransport);
            Assert.AreEqual(3, testValue.Interleaved.First);
            Assert.IsTrue(testValue.Interleaved.IsSecondPortPresent);
            Assert.AreEqual(4, testValue.Interleaved.Second);
        }
Beispiel #8
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());
        }
        public void Parse2()
        {
            RtspTransport testValue = RtspTransport.Parse("RTP/AVP/TCP;multicast;destination=test.example.com;ttl=234;ssrc=cd3b20a5");

            Assert.IsTrue(testValue.IsMulticast);
            Assert.AreEqual(RtspTransport.LowerTransportType.TCP, testValue.LowerTransport);
            Assert.AreEqual("test.example.com", testValue.Destination);
            Assert.AreEqual("cd3b20a5", testValue.SSrc);
            Assert.AreEqual("PLAY", testValue.Mode);
        }
Beispiel #10
0
 /// <summary>
 /// Asynchronously plays a stream.
 /// </summary>
 /// <param name="url">The url of a stream to play.</param>
 /// <param name="connectionTimeout">The connection timeout.</param>
 /// <exception cref="StreamPlayerException">Failed to play the stream.</exception>
 /// <param name="transport">RTSP transport protocol.</param>
 /// <param name="flags">RTSP flags.</param>
 internal void StartPlay(String url, TimeSpan connectionTimeout,
                         RtspTransport transport, RtspFlags flags)
 {
     if (_startPlayDelegate(url,
                            Convert.ToInt32(connectionTimeout.TotalMilliseconds),
                            Convert.ToInt32(transport),
                            Convert.ToInt32(flags)) != 0)
     {
         throw new StreamPlayerException("Failed to play the stream.");
     }
 }
        /// <summary>
        /// Asynchronously plays a stream.
        /// </summary>
        /// <param name="uri">The uri of a stream to play.</param>
        /// <param name="connectionTimeout"></param>
        /// <exception cref="Win32Exception">Failed to load the FFmpeg facade dll.</exception>
        /// <exception cref="StreamPlayerException">Failed to play the stream.</exception>
        /// <param name="transport">RTSP transport protocol.</param>
        /// <param name="flags">RTSP flags.</param>
        public void StartPlay(Uri uri, TimeSpan connectionTimeout,
                              RtspTransport transport, RtspFlags flags, FrameDelay framedelay)
        {
            if (IsPlaying)
            {
                Stop();
            }

            Player.StartPlay(uri.IsFile ? uri.LocalPath : uri.ToString(),
                             connectionTimeout, transport, flags, framedelay);
        }
Beispiel #12
0
        public void Parse4()
        {
            RtspTransport testValue = RtspTransport.Parse("RTP/AVP;unicast;destination=1.2.3.4;source=3.4.5.6;server_port=5000-5001;client_port=5003-5004");

            Assert.IsFalse(testValue.IsMulticast);
            Assert.AreEqual(RtspTransport.LowerTransportType.UDP, testValue.LowerTransport);
            Assert.AreEqual("1.2.3.4", testValue.Destination);
            Assert.AreEqual("3.4.5.6", testValue.Source);
            Assert.AreEqual(5000, testValue.ServerPort.First);
            Assert.AreEqual(5001, testValue.ServerPort.Second);
            Assert.AreEqual(5003, testValue.ClientPort.First);
            Assert.AreEqual(5004, testValue.ClientPort.Second);
        }
Beispiel #13
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());
        }
Beispiel #14
0
        public void StartReadStream()
        {
            try
            {
                LoadIni();

                Uri           u      = new Uri(m_strUri);
                TimeSpan      ts     = new TimeSpan(0, 0, 0, 0, m_nTimeOut);
                RtspTransport tsport = (RtspTransport)m_nTransPort;

                m_streamControl.StartPlay(u, ts, tsport, RtspFlags.None);
                m_camStatus = CamStatus.Connecting;
            }
            catch (Exception)
            {
            }
        }
Beispiel #15
0
        public void StartReadStream(string uri, int timeout_ms, int transport = 1) //transport : 1-TCP, 2-UDP, 0-NONE
        {
            m_strUri     = uri;
            m_nTimeOut   = timeout_ms;
            m_nTransPort = transport;

            Uri           u      = new Uri(uri);
            TimeSpan      ts     = new TimeSpan(0, 0, 0, 0, timeout_ms);
            RtspTransport tsport = (RtspTransport)transport;

            IniFile ini = new IniFile();

            ini.IniWriteValue(INISECT_RTSP, INIKEY_URI, uri, DefPath.VisionSetting);
            ini.IniWriteValue(INISECT_RTSP, INIKEY_TIMEOUT, timeout_ms.ToString(), DefPath.VisionSetting);
            ini.IniWriteValue(INISECT_RTSP, INIKEY_TRANSPORT, transport.ToString(), DefPath.VisionSetting);

            m_streamControl.StartPlay(u, ts, tsport, RtspFlags.None);
            m_camStatus = CamStatus.Connecting;
        }
Beispiel #16
0
        private void ProcessSetupResponse(RtspResponse message)
        {
            rtspSession = message.Session; // Session value used with Play, Pause, Teardown
            Logger.Info($"RTSP session: {rtspSession}");

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

                if (transport.IsMulticast)
                {
                    string multicastAddress = transport.Destination;
                    videoDataChannel = transport.Port.First;
                    videoRTCPChannel = transport.Port.Second;

                    // Create the Pair of UDP Sockets in Multicast mode
                    udpSocketPair = new UDPSocketPair(multicastAddress, videoDataChannel, multicastAddress, videoRTCPChannel);
                    udpSocketPair.DataReceived += RtpDataReceived;
                    udpSocketPair.Start();
                }
            }

            if (_suspendTransfer)
            {
                // Transfer suspended. Set state to playing but do not issue play request.
                // Play request will be sent upon resume.
                _currentState = State.Playing;
                return;
            }
            _currentState = State.Paused;
            PostRequest(new RtspRequestPlay
            {
                RtspUri = new Uri(rtspUrl),
                Session = rtspSession
            });
        }
Beispiel #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);
            }
        }
Beispiel #18
0
        private void MessageReceived(object sender, RtspChunkEventArgs e)
        {
            RtspResponse message = e.Message as RtspResponse;

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

                // Send DESCRIBE
                RtspRequest describe_message = new RtspRequestDescribe();
                describe_message.RtspUri = new Uri(_url);
                _client.SendMessage(describe_message);
            }


            if (message.OriginalRequest != null && message.OriginalRequest is RtspRequestDescribe)
            {
                Rtsp.Sdp.SdpFile sdp_data;
                using (StreamReader sdp_stream = new StreamReader(new MemoryStream(message.Data)))
                {
                    sdp_data = Rtsp.Sdp.SdpFile.Read(sdp_stream);
                }

                Uri video_uri = null;
                foreach (Rtsp.Sdp.Attribut attrib in sdp_data.Medias[0].Attributs)
                {
                    if (attrib.Key.Equals("control"))
                    {
                        video_uri = new Uri(attrib.Value);
                    }
                }

                RtspTransport transport = new RtspTransport()
                {
                    LowerTransport = RtspTransport.LowerTransportType.TCP,
                    Interleaved    = new PortCouple(0, 1),
                };

                // Generate SETUP messages
                RtspRequestSetup setup_message = new RtspRequestSetup();
                setup_message.RtspUri = video_uri;
                setup_message.AddTransport(transport);

                _client.SendMessage(setup_message);
            }


            if (message.OriginalRequest != null && message.OriginalRequest is RtspRequestSetup)
            {
                if (message.Timeout > 0 && message.Timeout > _keepaliveTimer.Interval / 1000)
                {
                    _keepaliveTimer.Interval = message.Timeout * 1000 / 2;
                }

                // Send PLAY
                RtspRequest play_message = new RtspRequestPlay();
                play_message.RtspUri = new Uri(_url);
                play_message.Session = message.Session;
                _client.SendMessage(play_message);
            }
        }
Beispiel #19
0
        // RTSP Messages are OPTIONS, DESCRIBE, SETUP, PLAY etc
        void Rtsp_MessageReceived(object sender, RtspChunkEventArgs e)
        {
            var message = e.Message as RtspResponse;

            //logger.Debug ("Received " + message.OriginalRequest.Method);

            // 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)
            {
                logger.Warn("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
                            }
                        }
                    }

                    //logger.Debug ("WWW Authorize parsed for " + auth_type + " " + realm + " " + nonce);
                }

                var 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 RtspRequestOptions)
            {
                string public_methods = message.Headers [RtspHeaderNames.Public];

                SupportedMethods.Clear();

                foreach (string method in public_methods.Split(','))
                {
                    SupportedMethods.Add(method.Trim());
                }

                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 = 5 * 1000;
                    keepalive_timer.Enabled  = true;

                    // Send DESCRIBE
                    var describe_message = new RtspRequestDescribe();
                    describe_message.RtspUri = new Uri(Url);
                    describe_message.AddAccept("application/sdp");

                    if (auth_type != null)
                    {
                        AddAuthorization(describe_message, Username, Password, auth_type, realm, nonce, Url);
                    }

                    rtsp_client.SendMessage(describe_message);
                }
            }

            // 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 Mictlanix.DotNet.Rtsp.Messages.RtspRequestDescribe)
            {
                // Got a reply for DESCRIBE
                if (!message.IsOk)
                {
                    logger.Warn("Got Error in DESCRIBE Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                // Examine the SDP
                //logger.Debug (Encoding.UTF8.GetString (message.Data));

                Sdp.SdpFile sdp_data;
                using (var sdp_stream = new StreamReader(new MemoryStream(message.Data))) {
                    sdp_data = 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 video = (sdp_data.Medias [x].MediaType == Mictlanix.DotNet.Rtsp.Sdp.Media.MediaTypes.video);

                    if (video && video_payload != -1)
                    {
                        continue;                                                   // have already matched an video payload
                    }
                    if (video)
                    {
                        // search the attributes for control, rtpmap and fmtp
                        // (fmtp only applies to video)
                        string            control = "";               // the "track" or "stream id"
                        Sdp.FmtpAttribute fmtp    = null;             // holds SPS and PPS in base64 (h264 video)

                        foreach (var attrib in sdp_data.Medias[x].Attributs)
                        {
                            if (attrib.Key.Equals("control"))
                            {
                                string sdp_control = attrib.Value;
                                if (sdp_control.ToLower().StartsWith("rtsp://", StringComparison.Ordinal))
                                {
                                    control = sdp_control;                                     //absolute path
                                }
                                else if (message.Headers.ContainsKey(RtspHeaderNames.ContentBase))
                                {
                                    control = message.Headers [RtspHeaderNames.ContentBase] + sdp_control;                                     // relative path
                                }
                                else
                                {
                                    control = Url + "/" + sdp_control;                                     // relative path
                                }
                            }
                            if (attrib.Key.Equals("fmtp"))
                            {
                                fmtp = attrib as Sdp.FmtpAttribute;
                            }
                            if (attrib.Key.Equals("rtpmap"))
                            {
                                var rtpmap = attrib as Sdp.RtpMapAttribute;

                                // Check if the Codec Used (EncodingName) is one we support
                                string [] valid_video_codecs = { "H264" };

                                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 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   = 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];
                                ParameterSetsReceived?.Invoke(sps, pps);
                            }
                        }

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

                        // Server interleaves the RTP packets over the RTSP connection
                        // TCP mode (RTP over RTSP)   Transport: RTP/AVP/TCP;interleaved=0-1
                        video_data_channel = 0;
                        video_rtcp_channel = 1;

                        var transport = new RtspTransport()
                        {
                            LowerTransport = RtspTransport.LowerTransportType.TCP,
                            Interleaved    = new PortCouple(video_data_channel, video_rtcp_channel),                           // Eg Channel 0 for video. Channel 1 for RTCP status reports
                        };

                        // Send SETUP
                        var setup_message = new 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);
                        }

                        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 RtspRequestSetup)
            {
                // Got Reply to SETUP
                if (!message.IsOk)
                {
                    logger.Warn("Got Error in SETUP Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                //logger.Debug ("Got reply from SETUP Session=" + message.Session);

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

                // Send PLAY
                RtspRequest play_message = new RtspRequestPlay();
                play_message.RtspUri = new Uri(Url);
                play_message.Session = RtspSession;

                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 RtspRequestPlay)
            {
                // Got Reply to PLAY
                if (!message.IsOk)
                {
                    logger.Warn("Got Error in PLAY Reply " + message.ReturnCode + " " + message.ReturnMessage);
                    return;
                }

                //logger.Debug ("Got reply from PLAY " + message.Command);
            }
        }
Beispiel #20
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;
                }
            }
        }
Beispiel #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());

            // 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);
            }
        }
Beispiel #22
0
        private RtspTransport RTSP_ConstructReplyTransport(RtspTransport transport, out UDPSocket udp_pair)
        {
            udp_pair = null;

            RtspTransport transport_reply = new 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)
            {
                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
                    try
                    {
                        _logger.Trace($"{Id} Start creating UDPSocket");
                        udp_pair = new Rtsp.UDPSocket(_ipAddress, 50000, 51000); // give a range of 500 pairs (1000 addresses) to try incase some address are in use
                        udp_pair.DataReceived    += UdpPair_DataReceived;
                        udp_pair.ControlReceived += UdpPair_ControlReceived;
                        udp_pair.Start(); // start listening for data on the UDP ports
                        _logger.Trace($"{Id} End creating UDPSocket");
                    }
                    catch
                    {
                        if (udp_pair != null)
                        {
                            ReleaseUDPSocket(udp_pair);
                            throw;
                        }
                    }


                    // 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._dataPort, udp_pair._controlPort);
                }
                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;
            }

            return(transport_reply);
        }
Beispiel #23
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);
            }
        }
        /// <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);
        }
Beispiel #25
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();
                        }
                    }
                }
            }
        }
        /// <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);
        }
Beispiel #27
0
        /// <summary>
        /// Constructs a stream.
        /// </summary>
        /// <param name="uri">The uri of a stream to play.</param>
        /// <param name="connectionTimeout">The connection timeout.</param>
        /// <param name="streamTimeout">The stream timeout.</param>
        /// <param name="transport">RTSP transport protocol.</param>
        /// <param name="flags">RTSP flags.</param>
        public static Stream FromUri(Uri uri,
                                     TimeSpan connectionTimeout, TimeSpan streamTimeout, RtspTransport transport, RtspFlags flags)
        {
            var type  = Load();
            var proxy = type.GetMethod("FromUri").Invoke(null,
                                                         new Object[] { uri, connectionTimeout, streamTimeout, transport, flags });

            return(new Stream(proxy));
        }
Beispiel #28
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);
            }
        }