/// <summary> /// Ends the client session /// </summary> /// <param name="request">The Teardown request</param> /// <param name="session">The session which recieved the request</param> internal void ProcessRtspTeardown(RtspMessage request, ClientSession session, bool sendResponse = true) { //If there was a location which was not a wildcard attempt to honor it. if (request.Location != RtspMessage.Wildcard) { RtpSource found = FindStreamByLocation(request.Location) as RtpSource; if (found == null) { ProcessLocationNotFoundRtspRequest(session, sendResponse); return; } if (false == AuthenticateRequest(request, found)) { ProcessAuthorizationRequired(found, session, sendResponse); return; } //Send the response var resp = session.ProcessTeardown(request, found); ////Keep track if LeaveOpen should be set (based on if the session still shared the socket) //if (false == (session.LeaveOpen = session.SharesSocket)) //{ // //if it doesn't then inform that close may occur? // resp.AppendOrSetHeader(RtspHeaders.Connection, "close"); //} ProcessSendRtspMessage(resp, session, sendResponse); session.ReleaseUnusedResources(); resp = null; //Attempt to remove the sessionId when nothing is playing... why? //10.4 SETUP says we would have to bundle pipelined requests. //That is contradictory. //if (session.Playing.Count == 0) session.SessionId = null; string connectionHeader = request[RtspHeaders.Connection]; if (false == string.IsNullOrWhiteSpace(connectionHeader)) { if (string.Compare(connectionHeader.Trim(), "close", true) == 0) { TryDisposeAndRemoveSession(session); } } } else { //Create the response var resp = session.CreateRtspResponse(request); //Todo //Make a RtpInfo header for each stream ending... //Stop transport level activity if required if (session.m_RtpClient != null && session.m_RtpClient.IsActive) session.m_RtpClient.SendGoodbyes(); //Remove all attachments and clear playing session.RemoveAllAttachmentsAndClearPlaying(); //Send the response ProcessSendRtspMessage(resp, session, sendResponse); //Release any unused resources at this point session.ReleaseUnusedResources(); //Remove the sessionId session.SessionId = null; resp = null; string connectionHeader = request[RtspHeaders.Connection]; if (false == string.IsNullOrWhiteSpace(connectionHeader)) { if (string.Compare(connectionHeader.Trim(), "close", true) == 0) { TryDisposeAndRemoveSession(session); } } } }
/// <summary> /// Handles the SET_PARAMETER RtspRequest /// </summary> /// <param name="request">The GET_PARAMETER RtspRequest to handle</param> /// <param name="ci">The RtspSession from which the request was receieved</param> internal void ProcessSetParameter(RtspMessage request, ClientSession session, bool sendResponse = true) { //Could be used for PTZ or other stuff //Should have a way to determine to forward send parameters... public bool ForwardSetParameter { get; set; } //Should have a way to call SendSetParamter on the source if required. //Should allow sever parameters to be set? using (var resp = session.CreateRtspResponse(request)) { //Content type ProcessSendRtspMessage(resp, session, sendResponse); } }
/// <summary> /// Provides the Options this server supports /// </summary> /// <param name="request"></param> /// <param name="session"></param> internal void ProcessRtspOptions(RtspMessage request, ClientSession session, bool sendResponse = true) { //See if the location requires a certain stream if (request.Location != RtspMessage.Wildcard && false == string.IsNullOrWhiteSpace(request.Location.LocalPath) && request.Location.LocalPath.Length > 1) { IMedia found = FindStreamByLocation(request.Location); //No stream with name if (found == null) { ProcessLocationNotFoundRtspRequest(session, sendResponse); return; } //See if RECORD is supported? } //Check for additional options of the stream... e.g. allow recording or not RtspMessage resp = session.CreateRtspResponse(request); resp.SetHeader(RtspHeaders.Public, "OPTIONS, DESCRIBE, SETUP, PLAY, PAUSE, TEARDOWN, GET_PARAMETER"); //Allow for Authorized resp.SetHeader(RtspHeaders.Allow, "ANNOUNCE, RECORD, SET_PARAMETER"); //Add from handlers? //if(m_RequestHandlers.Count > 0) string.Join(" ", m_RequestHandlers.Keys.ToArray()) //Should allow server to have certain options removed from this result //ClientSession.ProcessOptions ProcessSendRtspMessage(resp, session, sendResponse); resp = null; }
/// <summary> /// Decribes the requested stream /// </summary> /// <param name="request"></param> /// <param name="session"></param> internal void ProcessRtspDescribe(RtspMessage request, ClientSession session, bool sendResponse = true) { if (request.Location == RtspMessage.Wildcard) { var resp = session.CreateRtspResponse(request); ProcessSendRtspMessage(resp, session, sendResponse); resp = null; } else { string acceptHeader = request[RtspHeaders.Accept]; //If an Accept header was given it must reflect a content-type of SDP. if (false == string.IsNullOrWhiteSpace(acceptHeader) && string.Compare(acceptHeader, Sdp.SessionDescription.MimeType, true, System.Globalization.CultureInfo.InvariantCulture) > 0) { ProcessInvalidRtspRequest(session); return; } RtpSource found = FindStreamByLocation(request.Location) as RtpSource; if (found == null) { ProcessLocationNotFoundRtspRequest(session, sendResponse); return; } if (false == AuthenticateRequest(request, found)) { ProcessAuthorizationRequired(found, session, sendResponse); return; } if (false == found.Ready) { ProcessInvalidRtspRequest(session, RtspStatusCode.PreconditionFailed, null, sendResponse); return; } var resp = session.ProcessDescribe(request, found); ProcessSendRtspMessage(resp, session, sendResponse); resp = null; } }
internal void ProcessRedirect(RtspMessage request, ClientSession session, bool sendResponse = true) { var found = FindStreamByLocation(request.Location); if (found == null) { ProcessLocationNotFoundRtspRequest(session, sendResponse); return; } if (false == AuthenticateRequest(request, found)) { ProcessAuthorizationRequired(found, session, sendResponse); return; } if (false == found.Ready) { ProcessInvalidRtspRequest(session, RtspStatusCode.PreconditionFailed, null, sendResponse); return; } using (var resp = session.CreateRtspResponse(request)) { resp.Method = RtspMethod.REDIRECT; resp.AppendOrSetHeader(RtspHeaders.Location, "rtsp://" + session.LocalEndPoint.Address.ToString() + "/live/" + found.Id); ProcessSendRtspMessage(resp, session, sendResponse); } }
/// <summary> /// Sends a Rtsp Response on the given client session /// </summary> /// <param name="ci">The client session to send the response on</param> /// <param name="code">The status code of the response if other than BadRequest</param> //Should allow a header to be put into the response or a KeyValuePair<string,string> headers internal void ProcessInvalidRtspRequest(ClientSession session, RtspStatusCode code = RtspStatusCode.BadRequest, string body = null, bool sendResponse = true) { //Create and Send the response ProcessInvalidRtspRequest(session != null ? session.CreateRtspResponse(null, code, body) : new RtspMessage(RtspMessageType.Response) { StatusCode = code, Body = body, CSeq = Utility.Random.Next() }, session); }
/// <summary> /// Handles the GET_PARAMETER RtspRequest /// </summary> /// <param name="request">The GET_PARAMETER RtspRequest to handle</param> /// <param name="ci">The RtspSession from which the request was receieved</param> internal void ProcessGetParameter(RtspMessage request, ClientSession session, bool sendResponse = true) { //TODO Determine API //packets_sent //jitter //rtcp_interval var resp = session.CreateRtspResponse(request); resp.SetHeader(RtspHeaders.Connection, "Keep-Alive"); ProcessSendRtspMessage(resp, session, sendResponse); resp = null; }