コード例 #1
0
        //Todo, cleanup and allow existing Rtp and Rtcp socket.

        /// <summary>
        /// Will create a <see cref="RtpClient"/> based on the given parameters
        /// </summary>
        /// <param name="sessionDescription"></param>
        /// <param name="sharedMemory"></param>
        /// <param name="incomingEvents"></param>
        /// <param name="rtcpEnabled"></param>
        /// <returns></returns>
        public static RtpClient FromSessionDescription(Sdp.SessionDescription sessionDescription, Common.MemorySegment sharedMemory = null, bool incomingEvents = true, bool rtcpEnabled = true, System.Net.Sockets.Socket existingSocket = null, int?rtpPort = null, int?rtcpPort = null, int remoteSsrc = 0, int minimumSequentialRtpPackets = 2, bool connect = true, System.Action <System.Net.Sockets.Socket> configure = null)
        {
            if (Common.IDisposedExtensions.IsNullOrDisposed(sessionDescription))
            {
                throw new System.ArgumentNullException("sessionDescription");
            }

            Sdp.Lines.SessionConnectionLine connectionLine = new Sdp.Lines.SessionConnectionLine(sessionDescription.ConnectionLine);

            System.Net.IPAddress remoteIp = System.Net.IPAddress.Parse(connectionLine.Host), localIp;

            System.Net.NetworkInformation.NetworkInterface localInterface;

            //If the socket is NOT null and IS BOUND use the localIp of the same address family
            if (object.ReferenceEquals(existingSocket, null).Equals(false) && existingSocket.IsBound)
            {
                //If the socket is IP based
                if (existingSocket.LocalEndPoint is System.Net.IPEndPoint)
                {
                    //Take the localIp from the LocalEndPoint
                    localIp = (existingSocket.LocalEndPoint as System.Net.IPEndPoint).Address;
                }
                else
                {
                    throw new System.NotSupportedException("Please create an issue for your use case.");
                }
            }
            else // There is no socket existing.
            {
                //If the remote address is the broadcast address or the remote address is multicast
                if (System.Net.IPAddress.Broadcast.Equals(remoteIp) || IPAddressExtensions.IsMulticast(remoteIp))
                {
                    //This interface should be the interface you plan on using for the Rtp communication
                    localIp = SocketExtensions.GetFirstMulticastIPAddress(remoteIp.AddressFamily, out localInterface);
                }
                else
                {
                    //This interface should be the interface you plan on using for the Rtp communication
                    localIp = SocketExtensions.GetFirstUnicastIPAddress(remoteIp.AddressFamily, out localInterface);
                }
            }

            RtpClient client = new RtpClient(sharedMemory, incomingEvents);

            byte lastChannel = 0;

            //Todo, check for session level ssrc
            //if (remoteSsrc.Equals(0))
            //{
            //    //Sdp.SessionDescriptionLine ssrcLine = sessionDescription.SsrcGroupLine; // SsrcLine @ the session level could imply Group
            //}

            //For each MediaDescription in the SessionDescription
            foreach (Media.Sdp.MediaDescription md in sessionDescription.MediaDescriptions)
            {
                //Make a RtpClient.TransportContext from the MediaDescription being parsed.
                TransportContext tc = TransportContext.FromMediaDescription(sessionDescription, lastChannel++, lastChannel++, md,
                                                                            rtcpEnabled, remoteSsrc, minimumSequentialRtpPackets,
                                                                            localIp, remoteIp, //The localIp and remoteIp
                                                                            rtpPort, rtcpPort, //The remote ports to receive data from
                                                                            connect, existingSocket, configure);

                //Try to add the context
                try
                {
                    client.AddContext(tc);
                }
                catch (System.Exception ex)
                {
                    TaggedExceptionExtensions.RaiseTaggedException(tc, "See Tag, Could not add the created TransportContext.", ex);
                }
            }

            //Return the participant
            return(client);
        }
コード例 #2
0
        internal void ReleaseUnusedResources()
        {
            //Enumerate each context 'SETUP' in the session
            if (m_RtpClient != null)
            {
                //Iterate each context in the client
                foreach (var context in m_RtpClient.GetTransportContexts())
                {
                    //Could be a property of the transport.. (especially the rtpclient.)
                    //E.g. IsInactive

                    //If the context does not have any activity
                    if (false == context.HasAnyRecentActivity)
                    {
                        //Session level logger
                        //Context has no activity

                        //See if there is still a source for the context
                        RtpClient.TransportContext sourceContext = GetSourceContext(context.MediaDescription);

                        //If there was a source context with activity
                        if (sourceContext != null && sourceContext.HasAnyRecentActivity)
                        {
                            //Remove the attachment from the source context to the session context
                            RemoveSource(Attached[sourceContext]);

                            //Session level logger
                            //Removed Attachment for sourceContext.Id

                            //Remove the reference to the sourceContext
                            sourceContext = null;
                        }
                    }
                }
            }

            //Get rid of any attachment this ClientSession had which no longer have a context.
            foreach (IMediaSource source in Attached.Keys.ToList().Where(s=> GetSourceContext(s.MediaDescription) == null))
            {
                //Remove the attached media
                RemoveSource(source);
            }

            //Remove rtp theads
            if (Playing.Count == 0)
            {
                if (m_RtpClient != null)
                {
                    m_RtpClient.Dispose();
                    m_RtpClient = null;
                }
            }
        }
コード例 #3
0
        /// <summary>
        /// Sends the Rtcp Goodbye and detaches all sources
        /// </summary>
        public override void Dispose()
        {
            if (IsDisposed) return;

            base.Dispose();

            RemoveAllAttachmentsAndClearPlaying();

            //Mark as disconnected
            IsDisconnected = true;

            //Disconnect the RtpClient so it's not hanging around wasting resources for nothing
            if (m_RtpClient != null)
            {
                try
                {
                    m_RtpClient.InterleavedData -= m_RtpClient_InterleavedData;

                    m_RtpClient.Dispose();

                    m_RtpClient = null;
                }
                catch { }
            }

            if (m_Buffer != null)
            {
                try
                {
                    m_Buffer.Dispose();

                    m_Buffer = null;
                }
                catch { }
            }

            if (m_RtspSocket != null)
            {
                try
                {
                    if (false == LeaveOpen) m_RtspSocket.Dispose();

                    m_RtspSocket = null;
                }
                catch { }
            }

            if (LastRequest != null)
            {
                try
                {
                    LastRequest.Dispose();

                    LastRequest = null;
                }
                catch { }
            }

            if (LastResponse != null)
            {
                try
                {
                    LastResponse.Dispose();

                    LastResponse = null;
                }
                catch { }
            }

            m_Server = m_Contained = null;
        }
コード例 #4
0
        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <param name="sourceContext"></param>
        /// <returns></returns>
        /// //TODO Should be SourceMedia and SourceContext.
        internal RtspMessage ProcessSetup(RtspMessage request, RtpSource sourceStream, RtpClient.TransportContext sourceContext)
        {
            //Assign the sessionId now if it has not been assigned before.
            if (SessionId == null) SessionId = m_Id.GetHashCode().ToString();

            //We also have to send one back
            string returnTransportHeader = null;

            //Create a response
            RtspMessage response = CreateRtspResponse(request);

            Sdp.MediaDescription mediaDescription = sourceContext.MediaDescription;

            bool rtcpDisabled = sourceStream.m_DisableQOS;

            //Values in the header we need
            int clientRtpPort = -1, clientRtcpPort = -1, serverRtpPort = -1, serverRtcpPort = -1, localSsrc = 0, remoteSsrc = 0;

            //Cache this to prevent having to go to get it every time down the line
            IPAddress sourceIp = IPAddress.Any;

            string mode;

            bool unicast, multicast, interleaved, multiplexing;

            byte dataChannel = 0, controlChannel = 1;

            //Get the transport header
            string transportHeader = request[RtspHeaders.Transport];

            //If that is not present we cannot determine what transport the client wants
            if (string.IsNullOrWhiteSpace(transportHeader) ||
                false == (transportHeader.Contains("RTP")) ||
                false == RtspHeaders.TryParseTransportHeader(transportHeader,
                    out localSsrc, out sourceIp, out serverRtpPort, out serverRtcpPort, out clientRtpPort, out clientRtcpPort,
                    out interleaved, out dataChannel, out controlChannel, out mode, out unicast, out multicast))
            {
                return CreateRtspResponse(request, RtspStatusCode.BadRequest, "Invalid Transport Header");
            }

            //RTCP-mux: when RTSP 2.0 is official... (Along with Server Sent Messages)

            //Check if the ssrc was 0 which indicates any id
            if (localSsrc == 0) localSsrc = RFC3550.Random32((int)sourceContext.MediaDescription.MediaType);

            //Could also randomize the setupContext sequenceNumber here.
            //We need to make an TransportContext in response to a setup
            RtpClient.TransportContext setupContext = null;

             //Check for already setup stream and determine if the stream needs to be setup again or just updated
            if (Attached.ContainsKey(sourceContext))
            {
                //The contex may already existm should look first by ssrc.
                setupContext = m_RtpClient.GetContextForMediaDescription(sourceContext.MediaDescription);

                //If the context exists
                if (setupContext != null)
                {
                    //Update the ssrc  if it doesn't match.
                    if (localSsrc != 0 && setupContext.SynchronizationSourceIdentifier != localSsrc)
                    {
                        setupContext.SynchronizationSourceIdentifier = localSsrc;

                        if (remoteSsrc != 0 && setupContext.RemoteSynchronizationSourceIdentifier != remoteSsrc) setupContext.RemoteSynchronizationSourceIdentifier = remoteSsrc;
                    }

                    multicast = Media.Common.Extensions.IPAddress.IPAddressExtensions.IsMulticast(((IPEndPoint)setupContext.RemoteRtp).Address);

                    interleaved = setupContext.RtpSocket.ProtocolType == ProtocolType.Tcp && SharesSocket;

                    //Then indicate the information for that context in the return transport header.
                    returnTransportHeader = RtspHeaders.TransportHeader(setupContext.MediaDescription.MediaProtocol, setupContext.SynchronizationSourceIdentifier, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, ((IPEndPoint)setupContext.RemoteRtp).Port, ((IPEndPoint)setupContext.RemoteRtcp).Port, ((IPEndPoint)setupContext.LocalRtp).Port, ((IPEndPoint)setupContext.LocalRtcp).Port, false == multicast, multicast, null, interleaved, setupContext.DataChannel, setupContext.ControlChannel);

                    setupContext.LeaveOpen = interleaved;

                    //Attach logger (have option?)
                    m_RtpClient.Logger = m_Server.Logger;

                    goto UpdateContext;
                }
            }

            //Should determine intervals here for Rtcp from SessionDescription

            //Should determine if aggregate operation is allowed

            //Maybe setting up both udp and tcp at the same time? clientRtpPort needs to be nullable.
            //Maybe better to just give tokens from the function ..
            //Without explicitly checking for !interleaved VLC will recieve what it thinks are RTSP responses unless RTSP Interleaved is Forced.
            //Was trying to Quicktime to pickup RTSP Interleaved by default on the first response but it doesn't seem that easy (quick time tries to switch but fails?)

            //If the source does not force TCP and interleaved was not given and this is a unicast or multicast connection
            if (false == interleaved && (unicast || multicast))
            {

                //Check requested transport is allowed by server
                if (sourceStream.ForceTCP)//The client wanted Udp and Tcp was forced
                {
                    //Return the result
                    var result = CreateRtspResponse(request, RtspStatusCode.UnsupportedTransport);

                    //Indicate interleaved is required.
                    result.SetHeader(RtspHeaders.Transport, RtspHeaders.TransportHeader(RtpClient.RtpAvpProfileIdentifier + "/TCP", localSsrc, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, null, null, null, null, null, false, null, true, dataChannel, controlChannel));

                    return result;
                }

                //QuickTime debug

                if (clientRtpPort == 0) clientRtpPort = Media.Common.Extensions.Socket.SocketExtensions.FindOpenPort(ProtocolType.Udp, 30000, true);

                if (clientRtcpPort == 0) clientRtcpPort = clientRtpPort + 1;

                if (serverRtpPort == 0) serverRtpPort = Media.Common.Extensions.Socket.SocketExtensions.FindOpenPort(ProtocolType.Udp, 30000, true);

                if (serverRtcpPort == 0) serverRtcpPort = serverRtpPort + 1;

                //Ensure the ports are allowed to be used.
                if (m_Server.MaximumUdpPort.HasValue &&
                    (clientRtpPort > m_Server.MaximumUdpPort || clientRtcpPort > m_Server.MaximumUdpPort))
                {
                    //Handle port out of range
                    return CreateRtspResponse(request, RtspStatusCode.BadRequest, "Requested Udp Ports were out of range. Maximum Port = " + m_Server.MaximumUdpPort);
                }

                //Create sockets to reserve the ports.

                var localAddress = ((IPEndPoint)m_RtspSocket.LocalEndPoint).Address;

                Socket tempRtp = Media.Common.Extensions.Socket.SocketExtensions.ReservePort(SocketType.Dgram, ProtocolType.Udp, localAddress, clientRtpPort);

                Socket tempRtcp = Media.Common.Extensions.Socket.SocketExtensions.ReservePort(SocketType.Dgram, ProtocolType.Udp, localAddress, clientRtcpPort);

                //Check if the client was already created.
                if (m_RtpClient == null || m_RtpClient.IsDisposed)
                {
                    //Create a sender using a new segment on the existing buffer.
                    m_RtpClient = new RtpClient(new Common.MemorySegment(m_Buffer));

                    //Dont handle frame changed events from the client
                    m_RtpClient.FrameChangedEventsEnabled = false;

                    //Dont handle packets from the client
                    m_RtpClient.HandleIncomingRtpPackets = false;

                    //Attach the Interleaved data event
                    m_RtpClient.InterleavedData += m_RtpClient_InterleavedData;

                    //Attach logger (have option?)
                    m_RtpClient.Logger = m_Server.Logger;

                    //Use default data and control channel (Should be option?)
                    setupContext = new RtpClient.TransportContext(0, 1, localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);
                }
                else //The client was already created.
                {
                    //Have to calculate next data and control channel
                    RtpClient.TransportContext lastContext = m_RtpClient.GetTransportContexts().LastOrDefault();

                    if (lastContext != null) setupContext = new RtpClient.TransportContext((byte)(lastContext.DataChannel + 2), (byte)(lastContext.ControlChannel + 2), localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);
                    else setupContext = new RtpClient.TransportContext(dataChannel, controlChannel, localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);
                }

                //Initialize the Udp sockets
                setupContext.Initialize(localAddress, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, serverRtpPort, serverRtcpPort, clientRtpPort, clientRtcpPort);

                //Ensure the receive buffer size is updated for that context.
                Media.Common.ISocketReferenceExtensions.SetReceiveBufferSize(((Media.Common.ISocketReference)setupContext), m_Buffer.Count);

                ////Check if the punch packets made it out.
                //if ((setupContext.IsRtpEnabled && ((IPEndPoint)setupContext.RemoteRtp).Port == 0)
                //    ||
                //    (setupContext.IsRtcpEnabled && ((IPEndPoint)setupContext.RemoteRtcp).Port == 0))
                //{
                //    //Response should be a 461 or we should indicate the remote party is not yet listening in the response
                //    //Could also use StatusCode (100) with a reason phrase or header
                //}

                //Add the transportChannel
                m_RtpClient.TryAddContext(setupContext);

                //Create the returnTransportHeader (Should be setupContext.SynchronizationSourceIdentifier)
                returnTransportHeader = RtspHeaders.TransportHeader(RtpClient.RtpAvpProfileIdentifier, localSsrc, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, clientRtpPort, clientRtcpPort, serverRtpPort, serverRtcpPort, true, false, null, false, 0, 0);

                tempRtp.Dispose();

                tempRtp = null;

                tempRtcp.Dispose();

                tempRtcp = null;

            }

            //Check for 'interleaved' token or TCP being forced
            if (sourceStream.ForceTCP || interleaved)
            {
                //Check if the client was already created.
                if (m_RtpClient == null || m_RtpClient.IsDisposed)
                {
                    //Create a sender using a new segment on the existing buffer.
                    m_RtpClient = new RtpClient(new Common.MemorySegment(m_Buffer));

                    m_RtpClient.InterleavedData += m_RtpClient_InterleavedData;

                    //Attach logger (have option?)
                    m_RtpClient.Logger = m_Server.Logger;

                    #region Unused [Helps with debugging]

                    //m_RtpClient.RtcpPacketReceieved += m_RtpClient_RecievedRtcp;

                    //m_RtpClient.RtpPacketReceieved += m_RtpClient_RecievedRtp;

                    //m_RtpClient.RtcpPacketSent += m_RtpClient_SentRtcp;

                    #endregion

                    //Dont handle frame changed events from the client
                    m_RtpClient.FrameChangedEventsEnabled = false;

                    //Dont handle packets from the client
                    m_RtpClient.HandleIncomingRtpPackets = false;

                    //Create a new Interleave (don't use what was given as data or control channels)
                    setupContext = new RtpClient.TransportContext((byte)(dataChannel = 0), (byte)(controlChannel = 1), localSsrc, mediaDescription, m_RtspSocket, false == rtcpDisabled, remoteSsrc, 0);

                    //Add the transportChannel the client requested
                    m_RtpClient.TryAddContext(setupContext);

                    //Initialize the Interleaved Socket
                    setupContext.Initialize(m_RtspSocket, m_RtspSocket);
                }
                else //The client was already created
                {
                    //Have to calculate next data and control channel
                    RtpClient.TransportContext lastContext = m_RtpClient.GetTransportContexts().LastOrDefault();

                    //Don't use what was given as data or control channels
                    if (lastContext != null) setupContext = new RtpClient.TransportContext(dataChannel = (byte)(lastContext.DataChannel + 2), controlChannel = (byte)(lastContext.ControlChannel + 2), localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);
                    else setupContext = new RtpClient.TransportContext(dataChannel, controlChannel, localSsrc, mediaDescription, false == rtcpDisabled, remoteSsrc, 0);

                    //Add the transportChannel the client requested
                    m_RtpClient.TryAddContext(setupContext);

                    //Initialize the current TransportChannel with the interleaved Socket
                    setupContext.Initialize(m_RtspSocket, m_RtspSocket);
                }

                //Create the returnTransportHeader
                //returnTransportHeader = RtspHeaders.TransportHeader(RtpClient.RtpAvpProfileIdentifier + "/TCP", setupContext.SynchronizationSourceIdentifier, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, LocalEndPoint.Port, LocalEndPoint.Port, ((IPEndPoint)RemoteEndPoint).Port, ((IPEndPoint)RemoteEndPoint).Port, true, false, null, true, dataChannel, controlChannel);

                //Leave the socket open when disposing the RtpClient
                setupContext.LeaveOpen = true;

                returnTransportHeader = RtspHeaders.TransportHeader(RtpClient.RtpAvpProfileIdentifier + "/TCP", localSsrc, ((IPEndPoint)m_RtspSocket.RemoteEndPoint).Address, null, null, null, null, null, false, null, true, dataChannel, controlChannel);
            }

            //Add the new source
            Attached.Add(sourceContext, sourceStream);

            UpdateContext:

            //Synchronize the context sequence numbers
            setupContext.SequenceNumber = sourceContext.SequenceNumber;

            //Start and end times are always equal.
            setupContext.MediaStartTime = sourceContext.MediaStartTime;
            setupContext.MediaEndTime = sourceContext.MediaEndTime;

            //Set the returnTransportHeader to the value above
            response.SetHeader(RtspHeaders.Transport, returnTransportHeader);

            //Give the sessionid for the transport setup
            response.SetHeader(RtspHeaders.Session, SessionId);

            return response;
        }
コード例 #5
0
ファイル: RtpClient.cs プロジェクト: qinpengit/net7mma-111212
        /// <summary>
        /// Will create a <see cref="RtpClient"/> based on the given parameters
        /// </summary>
        /// <param name="sessionDescription"></param>
        /// <param name="sharedMemory"></param>
        /// <param name="incomingEvents"></param>
        /// <param name="rtcpEnabled"></param>
        /// <returns></returns>
        public static RtpClient FromSessionDescription(Sdp.SessionDescription sessionDescription, Common.MemorySegment sharedMemory = null, bool incomingEvents = true, bool rtcpEnabled = true, Socket existingSocket = null, int? rtpPort = null, int? rtcpPort = null, int remoteSsrc = 0, int minimumSequentialRtpPackets = 2)
        {
            if (sessionDescription == null) throw new ArgumentNullException("sessionDescription");

            Sdp.Lines.SessionConnectionLine connectionLine = new Sdp.Lines.SessionConnectionLine(sessionDescription.ConnectionLine);

            IPAddress remoteIp = IPAddress.Parse(connectionLine.IPAddress), localIp = Media.Common.Extensions.Socket.SocketExtensions.GetFirstIPAddress(remoteIp.AddressFamily);

            RtpClient participant = new RtpClient(sharedMemory, incomingEvents);

            byte lastChannel = 0;

            bool hasSocket = existingSocket != null;

            foreach (Media.Sdp.MediaDescription md in sessionDescription.MediaDescriptions)
            {
                TransportContext tc = TransportContext.FromMediaDescription(sessionDescription, lastChannel++, lastChannel++, md, rtcpEnabled, remoteSsrc, minimumSequentialRtpPackets);

                //Find range info in the SDP
                var rangeInfo = md.RangeLine;

                //If there is a range directive
                if (rangeInfo == null)
                {
                    rangeInfo = sessionDescription.RangeLine;
                    if (rangeInfo != null)
                    {
                        string type;
                        Sdp.SessionDescription.TryParseRange(rangeInfo.Parts[0], out type, out tc.m_StartTime, out tc.m_EndTime);
                    }
                    //else if (sessionDescription.TimeDescriptions.Count > 0)
                    //{
                    //tc.MediaStartTime = TimeSpan.FromMilliseconds();
                    //tc.MediaEndTime = TimeSpan.FromMilliseconds();
                    //}
                }

                //Check for udp if no existing socket was given
                if (false == hasSocket && string.Compare(md.MediaProtocol, Media.Rtp.RtpClient.RtpAvpProfileIdentifier, true) == 0)
                {
                    int localPort = Media.Common.Extensions.Socket.SocketExtensions.FindOpenPort(ProtocolType.Udp);
                    tc.Initialize(localIp, remoteIp, localPort++, localPort++, rtpPort ?? md.MediaPort, rtcpPort ?? md.MediaPort + 1);
                }
                else if (hasSocket)//If had a socket use it
                {
                    tc.Initialize(existingSocket);
                }
                else
                {
                    tc.Initialize(localIp, remoteIp, rtpPort ?? md.MediaPort);
                }

                //Try to add the context

                try
                {
                    participant.AddContext(tc);
                }
                catch (Exception ex)
                {
                    Media.Common.Extensions.Exception.ExceptionExtensions.TryRaiseTaggedException(tc, "See Tag, Could not add the created TransportContext.", ex);
                }
            }

            //Return the participant
            return participant;
        }
コード例 #6
0
ファイル: RtpClient.cs プロジェクト: qinpengit/net7mma-111212
 public static bool EnableFeedbackReports(RtpClient client)
 {
     if (FeedbackInstances.Add(client))
     {
         client.RtcpPacketReceieved += SendFeedback;
         return true;
     }
     return false;
 }
コード例 #7
0
ファイル: RtpClient.cs プロジェクト: qinpengit/net7mma-111212
 public static bool DisableFeedbackReports(RtpClient client)
 {
     if (FeedbackInstances.Remove(client))
     {
         client.RtcpPacketReceieved -= SendFeedback;
         return true;
     }
     return false;
 }
コード例 #8
0
ファイル: RtpClient.cs プロジェクト: qinpengit/net7mma-111212
        /// <summary>
        /// Returns the amount of bytes read to completely read the application layer framed data
        /// Where a negitive return value indicates no more data remains.
        /// </summary>
        /// <param name="received">How much data was received</param>
        /// <param name="frameChannel">The output of reading a frameChannel</param>
        /// <param name="context">The context assoicated with the frameChannel</param>
        /// <param name="offset">The reference to offset to look for framing data</param>
        /// <param name="raisedEvent">Indicates if an event was raised</param>
        /// <param name="buffer">The optional buffer to use.</param>
        /// <returns>The amount of bytes the frame data SHOULD have</returns>
        int ReadApplicationLayerFraming(int received, out byte frameChannel, out RtpClient.TransportContext context, ref int offset, out bool raisedEvent, byte[] buffer = null)
        {
            //There is no relevant TransportContext assoicated yet.
            context = null;

            //The channel of the frame - The Framing Method
            frameChannel = default(byte);

            raisedEvent = false;

            if (received <= 0) return -1;

            buffer = buffer ?? m_Buffer.Array;

            int bufferLength = buffer.Length, bufferOffset = offset;

            //Look for the frame control octet
            int startOfFrame = Array.IndexOf<byte>(buffer, BigEndianFrameControl, bufferOffset, received);

            int frameLength = 0;

            //If not found everything belongs to the upper layer
            if (startOfFrame == -1)
            {
                //System.Diagnostics.Debug.WriteLine("Interleaving: " + received);
                OnInterleavedData(buffer, bufferOffset, received);

                raisedEvent = true;

                //Indicate the amount of data consumed.
                return received;
            }
            else if (startOfFrame > offset) // If the start of the frame is not at the beginning of the buffer
            {
                //Determine the amount of data which belongs to the upper layer
                int upperLayerData = startOfFrame - bufferOffset;

                //System.Diagnostics.Debug.WriteLine("Moved To = " + startOfFrame + " Of = " + received + " - Bytes = " + upperLayerData + " = " + Encoding.ASCII.GetString(m_Buffer, mOffset, startOfFrame - mOffset));

                OnInterleavedData(buffer, bufferOffset, upperLayerData);

                raisedEvent = true;

                //Indicate length from offset until next possible frame. (should always be positive, if somehow -1 is returned this will signal a end of buffer to callers)

                //If there is more data related to upperLayerData it will be evented in the next run. (See RtspClient ProcessInterleaveData notes)
                return upperLayerData;
            }

            //If there is not enough data for a frame header return
            if (bufferOffset + InterleavedOverhead > bufferLength) return -1;

            //Todo Determine from Context to use control channel and length. (Check MediaDescription)
            //NEEDS TO HANDLE CASES WHERE RFC4571 Framing are in play and no $ or Channel are used....

            //The amount of data needed for the frame comes from TryReadFrameHeader
            frameLength = TryReadFrameHeader(buffer, bufferOffset, out frameChannel, true, BigEndianFrameControl, true);

            //Assign a context if there is a frame of any size
            if (frameLength >= 0)
            {
                //Assign the context
                context = GetContextByChannels(frameChannel);

                //Increase the result by the size of the header
                frameLength += InterleavedOverhead;
            }

            //Return the amount of bytes or -1 if any error occured.
            return frameLength;
        }