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