/// <summary> /// Constructs a new PayloadSpecificFeedbackReport from the given <see cref="RtcpHeader"/> and payload. /// Changes to the header and payload are immediately reflected in this instance. /// </summary> /// <param name="header"></param> /// <param name="payload"></param> public PayloadSpecificFeedbackReport(RtcpHeader header, Common.MemorySegment payload, bool shouldDipose = true) : base(header, payload, shouldDipose) { if (Header.PayloadType != PayloadType) { throw new System.ArgumentException("Header.PayloadType is not equal to the expected type of 206.", "reference"); } }
/// <summary> /// Allocates 24 octets to represent this ReportBlock instance. /// </summary> ReportBlock(bool shouldDispose = true) { m_OwnedOctets = new byte[Size]; Memory = new Common.MemorySegment(m_OwnedOctets, 0, Size); ShouldDispose = shouldDispose; }
/// <summary> /// Constructs a new ApplicationSpecificReport from the given <see cref="RtcpHeader"/> and payload. /// Changes to the header and payload are immediately reflected in this instance. /// </summary> /// <param name="header">The header</param> /// <param name="payload">The payload</param> public ApplicationSpecificReport(RtcpHeader header, Common.MemorySegment payload, bool shouldDispose = true) : base(header, payload, shouldDispose) { if (Header.PayloadType != PayloadType) { throw new ArgumentException("Header.PayloadType is not equal to the expected type of 204.", "reference"); } }
/// <summary> /// Constructs a new SendersReport from the given <see cref="RtcpHeader"/> and payload. /// Changes to the header and payload are immediately reflected in this instance. /// </summary> /// <param name="header"></param> /// <param name="payload"></param> public SendersReport(RtcpHeader header, Common.MemorySegment payload, bool shouldDipose = true) : base(header, payload, shouldDipose) { if (Header.PayloadType != PayloadType) { throw new ArgumentException("Header.PayloadType is not equal to the expected type of 200.", "reference"); } //RtcpReportExtensions.VerifyPayloadType(this); }
/// <summary> /// Creates a new ReportBlock instance from the given existing reference. /// Throws a ArgumentNullException if <paramref name="reference"/> is null. /// </summary> /// <param name="reference">A reference to a ReportBlock instance.</param> public ReportBlock(ReportBlock reference) { if (reference == null) { throw new ArgumentNullException(); } Memory = reference.Memory; }
public BinaryContent(Common.MemorySegment data, string name = null) : base(name) { if (data == null || data.Count == 0) { throw new ArgumentException("data cannot be null or empty"); } Data = data; }
public MultipartContent(Common.MemorySegment boundary) { if (boundary == null || boundary.Count == 0) { throw new ArgumentException("boundary cannot be null or empty"); } Boundary = boundary; }
public static void DisableTcpKeepAlive(System.Net.Sockets.Socket socket) { using (var optionMemory = new Common.MemorySegment(KeepAliveSize)) { Common.Binary.Write32(optionMemory.Array, 0, false, 0); int result = socket.IOControl(System.Net.Sockets.IOControlCode.KeepAliveValues, optionMemory.Array, optionMemory.Array); } }
/// <summary> /// Creates a RtcpPacket instance from the given parameters by copying data. /// </summary> /// <param name="buffer">The buffer which contains the binary RtcpPacket to decode</param> /// <param name="offset">The offset to start decoding</param> public RtcpPacket(byte[] buffer, int offset, int count, bool shouldDispose = true) { //The instance owns the header ShouldDispose = m_OwnsHeader = shouldDispose; //Create the header Header = new RtcpHeader(buffer, offset); //Determine the amount of bytes in the header and packet int headerLength = RtcpHeader.Length, packetLength = Header.LengthInWordsMinusOne; switch (packetLength) { case ushort.MinValue: { Payload = Common.MemorySegment.Empty; if (false == Padding) { return; } break; } default: { //Header has another word headerLength = Binary.BytesPerLong; //Packet length is given by the LengthInWordsMinusOne + 1 * 4 packetLength = ((ushort)((packetLength + 1) * 4)); //If there is no data in the payload don't consume it if (packetLength < headerLength) { goto case ushort.MinValue; } break; } } //ssrc doesn't technically coun't //int nonHeaderBytes = packetLength - headerLength; //Project the octets in the sequence taking the minimum of the octets present and the octets required as indicated by the header. //m_OwnedOctets = buffer.Skip(offset + headerLength).Take(nonHeaderBytes < 0 ? buffer.Length + nonHeaderBytes : nonHeaderBytes).ToArray(); //The Payload property must be assigned otherwise the properties will not function in the instance. //Payload = new Common.MemorySegment(m_OwnedOctets, shouldDispose); Payload = new Common.MemorySegment(buffer, offset + headerLength, count - headerLength, shouldDispose); m_OwnedOctets = Payload.Array; }
/// <summary> /// Creates a new ReportBlock instance from the given existing reference. /// Throws a ArgumentNullException if <paramref name="reference"/> is null. /// </summary> /// <param name="reference">A reference to a ReportBlock instance.</param> public ReportBlock(ReportBlock reference, bool shouldDispose = true) : base(shouldDispose) { if (reference == null) { throw new ArgumentNullException(); } Memory = reference.Memory; }
protected virtual void ParseProgramStreamMap(Container.Node node) { if (node.Identifier[3] != Mpeg.StreamTypes.ProgramStreamMap) { return; } int dataLength = node.Data.Length; if (dataLength < 4) { return; } byte mapId = node.Data[0]; byte reserved = node.Data[1]; ushort infoLength = Common.Binary.ReadU16(node.Data, 2, Common.Binary.IsLittleEndian); ushort mapLength = Common.Binary.ReadU16(node.Data, 4 + infoLength, Common.Binary.IsLittleEndian); int offset = 4 + infoLength + 2; int crcOffset = dataLength - 4; //While data remains in the map while (offset < crcOffset) { //Find out the type of item it is byte esType = node.Data[offset++]; //Find out the elementary stream id byte esId = node.Data[offset++]; //find out how long the info is ushort esInfoLength = Common.Binary.ReadU16(node.Data, offset, Common.Binary.IsLittleEndian); //Get a array containing the info Common.MemorySegment esData = esInfoLength == 0 ? Media.Common.MemorySegment.Empty : new Common.MemorySegment(node.Data, offset, esInfoLength); //node.Data.Skip(offset).Take(esInfoLength).ToArray(); //Create the entry var entry = new Tuple <byte, Common.MemorySegment>(esType, esData); //should keep entries until crc is updated if present and then insert. //Add it to the ProgramStreams m_ProgramStreams.AddOrUpdate(esId, entry, (id, old) => entry); //Move the offset offset += 2 + esInfoLength; } //Map crc }
public static bool IsValidPacket(this IRtpProfileInformation profileInfomation, RtpPacket packet) { if (Common.IDisposedExtensions.IsNullOrDisposed(packet)) { return(false); } Common.MemorySegment packetData = packet.PayloadDataSegment; return(packetData.Count > profileInfomation.MinimumHeaderSize && packetData.Count > profileInfomation.MaximumHeaderSize); }
public RtcpHeader(Common.MemorySegment memory, int additionalOffset = 0) { if (Math.Abs(memory.Count - additionalOffset) < RtcpHeader.Length) { throw new ArgumentException("memory must contain at least 4 elements", "memory"); } First16Bits = new Media.RFC3550.CommonHeaderBits(memory, additionalOffset); //das infamous clamp max min PointerToLast6Bytes = new Common.MemorySegment(memory.Array, memory.Offset + additionalOffset + RFC3550.CommonHeaderBits.Size, Math.Max(Math.Min(memory.Count - additionalOffset - RFC3550.CommonHeaderBits.Size, 6), RtcpHeader.Length)); }
public virtual int CompleteFrom(System.Net.Sockets.Socket socket, Common.MemorySegment buffer) { if (IsReadOnly) { throw new InvalidOperationException("Cannot modify a RtcpPacket when IsReadOnly is false."); } //If the packet is complete then return if (IsDisposed || IsComplete) { return(0); } //Calulcate the amount of octets remaining in the RtcpPacket including the header int octetsRemaining = ((ushort)(Header.LengthInWordsMinusOne + 1)) * 4 - Length, offset = Payload != null ? Payload.Count : 0; if (octetsRemaining > 0) { //There is not enough room in the array to finish the packet if (Payload.Count < octetsRemaining) { //Allocte the memory for the required data if (m_OwnedOctets == null) { m_OwnedOctets = new byte[octetsRemaining]; } else { m_OwnedOctets = m_OwnedOctets.Concat(new byte[octetsRemaining]).ToArray(); } } System.Net.Sockets.SocketError error; int recieved = 0; //Read from the stream, decrementing from octetsRemaining what was read. while (octetsRemaining > 0) { int rec = Media.Common.Extensions.Socket.SocketExtensions.AlignedReceive(m_OwnedOctets, offset, octetsRemaining, socket, out error); offset += rec; octetsRemaining -= rec; recieved += rec; } //Re-allocate the segment around the received data. Payload = new Common.MemorySegment(m_OwnedOctets, 0, m_OwnedOctets.Length); return(recieved); } return(0); }
public static void EnableTcpKeepAlive(System.Net.Sockets.Socket socket, int time, int interval) { using (var optionMemory = new Common.MemorySegment(KeepAliveSize)) { Common.Binary.Write32(optionMemory.Array, 0, false, 1); Common.Binary.Write32(optionMemory.Array, Common.Binary.BytesPerInteger, false, time); Common.Binary.Write32(optionMemory.Array, Common.Binary.BytesPerLong, false, interval); int result = socket.IOControl(System.Net.Sockets.IOControlCode.KeepAliveValues, optionMemory.Array, optionMemory.Array); } }
/// <summary> /// Reads an instance of the RtpHeader class and copies 12 octets which make up the RtpHeader. /// </summary> /// <param name="octets">A reference to a byte array which contains at least 12 octets to copy.</param> public RtpHeader(byte[] octets, int offset = 0) { //If the octets reference is null throw an exception if (octets == null) { throw new ArgumentNullException("octets"); } //Determine the length of the array int octetsLength = octets.Length; //Check range if (offset > octetsLength) { throw new ArgumentOutOfRangeException("offset", "Cannot be greater than the length of octets"); } //Check for the amount of octets required to build a RtpHeader given by the delination of the offset //if (octetsLength == 0 || octetsLength - offset < Length) throw new ArgumentException("octets must contain at least 12 elements given the deleniation of the offset parameter.", "octets"); if (octetsLength == 0) { throw new ArgumentException("octets must contain at least 1 element given the deleniation of the offset parameter.", "octets"); } bool hasMoreThanOnebyte = octetsLength > 1; if (hasMoreThanOnebyte) { //Read a managed representation of the first two octets which are stored in Big ByteOrder / Network Byte Order First16Bits = new Media.RFC3550.CommonHeaderBits(octets[offset + 0], octets[offset + 1]); //Allocate space for the other 10 octets Last10Bytes = hasMoreThanOnebyte ? new byte[10] : Media.Common.MemorySegment.EmptyBytes; //Copy the remaining bytes of the header which consist of the //SequenceNumber (2 octets / U16) //Timestamp (4 octets / U32) //SSRC (4 octets / U32) Array.Copy(octets, offset + 2, Last10Bytes, 0, Math.Min(10, octetsLength - 2)); } else { //Read a managed representation of the first two octets which are stored in Big ByteOrder / Network Byte Order First16Bits = new Media.RFC3550.CommonHeaderBits(octets[offset + 0], default(byte)); //Allocate space for the other 10 octets Last10Bytes = Media.Common.MemorySegment.EmptyBytes; } PointerToLast10Bytes = new MemorySegment(Last10Bytes, 0, Last10Bytes.Length); }
public RtcpHeader(int version, int payloadType, bool padding, int blockCount) { First16Bits = new Media.RFC3550.CommonHeaderBits(version, padding, false, false, payloadType, (byte)blockCount); Last6Bytes = new byte[6]; PointerToLast6Bytes = new Common.MemorySegment(Last6Bytes, 0, 6); //The default value must be set into the LengthInWords field otherwise it will reflect 0 if (blockCount == 0) { LengthInWordsMinusOne = RtcpHeader.MaximumLengthInWords; // ushort (0 - 1) } }
/// <summary> /// Creates an RtpExtension from the given binary data which must include the Flags and LengthInWords at the propper offsets. /// Data is copied from offset to count. /// </summary> /// <param name="binary">The binary data of the extensions</param> /// <param name="offset">The amount of bytes to skip in binary</param> /// <param name="count">The amount of bytes to copy from binary</param> public RtpExtension(byte[] binary, int offset, int count) { if (binary == null) { throw new ArgumentNullException("binary"); } else if (binary.Length < MinimumSize) { throw InvalidExtension; } //Atleast 4 octets are contained in binary m_MemorySegment = new Common.MemorySegment(binary, offset, count); }
public RtpHeader(int version, bool padding, bool extension) { First16Bits = new Media.RFC3550.CommonHeaderBits(version, padding, extension); //Allocate space for the other 10 octets Last10Bytes = new byte[10]; PointerToLast10Bytes = new Common.MemorySegment(Last10Bytes, 0, 10); Version = version; Padding = padding; Extension = extension; }
public Node(IMediaContainer master, Common.MemorySegment identifierPointer, int identifierSize, int lengthSize, long offset, long size, bool complete, bool shouldDispose = true) : base(shouldDispose) { Master = master; DataOffset = offset; IdentifierPointer = identifierPointer; Identifier = IdentifierPointer.Array; IdentifierSize = identifierSize; LengthSize = lengthSize; DataSize = size; IsComplete = complete; //Should calulcate here? }
/// <summary> /// Creates an exact copy of the RtpHeader from the given RtpHeader /// </summary> /// <param name="other">The RtpHeader to copy</param> /// <param name="reference">A value indicating if the RtpHeader given should be referenced or copied.</param> public RtpHeader(RtpHeader other, bool reference) { if (reference) { First16Bits = other.First16Bits; Last10Bytes = other.Last10Bytes; PointerToLast10Bytes = other.PointerToLast10Bytes; } else { First16Bits = new Media.RFC3550.CommonHeaderBits(other.First16Bits); Last10Bytes = new byte[10]; PointerToLast10Bytes = new Common.MemorySegment(Last10Bytes, 0, 10); other.Last10Bytes.CopyTo(Last10Bytes, 0); } }
public RtpHeader(RtpHeader other, bool reference, bool shouldDispose = true) : base(shouldDispose) { if (reference) { First16Bits = other.First16Bits; Last10Bytes = other.Last10Bytes; SegmentToLast10Bytes = other.SegmentToLast10Bytes; } else { First16Bits = new Media.RFC3550.CommonHeaderBits(other.First16Bits); Last10Bytes = new byte[10]; SegmentToLast10Bytes = new Common.MemorySegment(Last10Bytes, 0, 10); other.Last10Bytes.CopyTo(Last10Bytes, 0); } }
public MediaBuffer(MediaFormat mediaFormat, Common.MemorySegment data, Media.Codec.Interfaces.ICodec codec = null, long timestamp = 0, bool shouldDispose = true) : base(shouldDispose) { MediaFormat = mediaFormat; Codec = codec; Data = data; if (Data.Count < SampleLength) { throw new System.InvalidOperationException(string.Format("Insufficient Data for Sample, found: {0}, expected: {1}", data.Count, SampleLength)); } Timestamp = timestamp; //SampleCount = 1; }
/// <summary> /// Constucts a Node instance from the given parameters /// </summary> /// <param name="master"></param> /// <param name="identifier"></param> /// <param name="offset"></param> /// <param name="size"></param> /// <param name="complete"></param> public Node(IMediaContainer master, byte[] identifier, int lengthSize, long offset, long size, bool complete, bool shouldDispose = true) : base(shouldDispose) { if (master == null) { throw new ArgumentNullException("master"); } if (identifier == null) { throw new ArgumentNullException("identifier"); } Master = master; DataOffset = offset; Identifier = identifier; IdentifierPointer = new Common.MemorySegment(identifier); IdentifierSize = identifier.Length; LengthSize = lengthSize; DataSize = size; IsComplete = complete; //Should calulcate here? }
/// <summary> /// Copies the given octets to the Payload before any Padding and calls <see cref="SetLengthInWordsMinusOne"/>. /// </summary> /// <param name="octets">The octets to add</param> /// <param name="offset">The offset to start copying</param> /// <param name="count">The amount of bytes to copy</param> internal protected virtual void AddBytesToPayload(IEnumerable <byte> octets, int offset = 0, int count = int.MaxValue, bool setLength = true) //overload for padd if necessary? { if (IsReadOnly) { throw new InvalidOperationException("Can only set the AddBytesToPayload when IsReadOnly is false."); } //Build a seqeuence from the existing octets and the data in the ReportBlock //If there are existing owned octets (which may include padding) if (Padding) { //Determine the amount of bytes in the payload int payloadCount = Payload.Count, //Determine the padding octets offset paddingOctets = PaddingOctets, //Determine the amount of octets in the payload payloadOctets = payloadCount - paddingOctets; //The owned octets is a projection of the Payload existing, without the padding combined with the given octets from offset to count and subsequently the paddingOctets after the payload m_OwnedOctets = Enumerable.Concat(Payload.Take(payloadOctets), octets.Skip(offset).Take(count - offset)) .Concat(Payload.Skip(payloadOctets).Take(paddingOctets)).ToArray(); } else if (m_OwnedOctets == null) { m_OwnedOctets = octets.Skip(offset).Take(count - offset).ToArray(); } else { m_OwnedOctets = Enumerable.Concat(m_OwnedOctets, octets.Skip(offset).Take(count - offset)).ToArray(); } //Create a pointer to the owned octets. Payload = new Common.MemorySegment(m_OwnedOctets); //Set the length in words minus one in the header if (setLength) { SetLengthInWordsMinusOne(); } }
public RtcpHeader(byte[] octets, int offset = 0, bool shouldDispose = true) : base(shouldDispose) { //If the octets reference is null throw an exception if (octets == null) { throw new ArgumentNullException("octets"); } //Determine the length of the array int octetsLength = octets.Length, availableOctets = octetsLength - offset; //Check range if (offset > octetsLength) { throw new ArgumentOutOfRangeException("offset", "Cannot be greater than the length of octets"); } //Check for the amount of octets required to build a RtcpHeader given by the delination of the offset if (octetsLength == 0 || availableOctets < RtcpHeader.Length) { throw new ArgumentException("octets must contain at least 4 elements given the deleniation of the offset parameter.", "octets"); } //Read a managed representation of the first two octets which are stored in Big ByteOrder / Network Byte Order First16Bits = new Media.RFC3550.CommonHeaderBits(octets, offset); //Allocate space for the other 6 octets which consist of the //LengthInWordsMinusOne (16 bits) //SynchronizationSourceIdentifier (32 bits) // 48 Bits = 6 bytes Last6Bytes = new byte[6]; //Copy the remaining bytes of the header which consist of the aformentioned properties //If the LengthInWords is FFFF then this is extreanous and probably belongs to any padding... Array.Copy(octets, offset + RFC3550.CommonHeaderBits.Size, Last6Bytes, 0, Binary.Min(6, availableOctets - RFC3550.CommonHeaderBits.Size)); //Make a pointer to the last 6 bytes SegmentToLast6Bytes = new Common.MemorySegment(Last6Bytes, 0, 6); }
public override void Dispose() { base.Dispose(); if (ShouldDispose) { //Dispose the instance First16Bits.Dispose(); //Remove the reference to the CommonHeaderBits instance First16Bits = null; //Invalidate the pointer PointerToLast6Bytes.Dispose(); PointerToLast6Bytes = null; //Remove the reference to the allocated array. Last6Bytes = null; } }
public RtpHeader(byte[] octets, int offset = 0, bool shouldDispose = true) : base(shouldDispose) { //Determine the length of the array long octetsLength; //If the octets reference is null throw an exception if (Common.Extensions.Array.ArrayExtensions.IsNullOrEmpty(octets, out octetsLength)) { throw new ArgumentException("octets must not be null and must contain at least 1 element given the deleniation of the offset parameter.", "octets"); } //Check range if (offset > octetsLength) { throw new ArgumentOutOfRangeException("offset", "Cannot be greater than the length of octets"); } //Todo, should not matter since this header instance would be allowed to be modified, saving the allocation here is not necessary. //The check is only relevent because octets my have less than 2 bytes which cannot be handled without exception in the CommonHeaderBits //Read a managed representation of the first two octets which are stored in Big ByteOrder / Network Byte Order First16Bits = octetsLength == 1 ? new Media.RFC3550.CommonHeaderBits(octets[offset], default(byte)) : new Media.RFC3550.CommonHeaderBits(octets, offset); //Allocate space for the other 10 octets Last10Bytes = new byte[10]; //If there are octets for those bytes in the source array if (octetsLength > 2) { //Copy the remaining bytes of the header which consist of the //SequenceNumber (2 octets / U16) //Timestamp (4 octets / U32) //SSRC (4 octets / U32) Array.Copy(octets, offset + 2, Last10Bytes, 0, Math.Min(10, octetsLength - 2)); } //Assign the segment SegmentToLast10Bytes = new MemorySegment(Last10Bytes, 0, Last10Bytes.Length); }
/// <summary> /// Creates a new RtpExtension from the given options and optional data. /// </summary> /// <param name="sizeInBytes">The known size of the RtpExtension in bytes. The LengthInWords property will reflect this value divided by 4.</param> /// <param name="data">The optional extension data itself not including the Flags or LengthInWords fields.</param> /// <param name="offset">The optional offset into data to being copying.</param> public RtpExtension(int sizeInBytes, short flags = 0, byte[] data = null, int offset = 0) { //Allocate memory for the binary m_MemorySegment = new Common.MemorySegment(new byte[MinimumSize + sizeInBytes], 0, MinimumSize + sizeInBytes); //If there are any flags set them if (flags > 0) { Flags = flags; } //If there is any Extension data then set the LengthInWords field if (sizeInBytes > 0) { LengthInWords = (ushort)(sizeInBytes / MinimumSize); //10 = 2.5 becomes (3 words => 12 bytes) } //If the data is not null and the size in bytes a positive value if (data != null && sizeInBytes > 0) { //Copy the data from to the binary taking only the amount of bytes which can be read within the bounds of the vector. Array.Copy(data, offset, m_MemorySegment.Array, 0, Math.Min(data.Length, sizeInBytes)); } }
public int CompleteFrom(System.Net.Sockets.Socket socket, Common.MemorySegment buffer) { if (IsComplete) { return(0); } int contained = m_Packet.Length, needed = MessageLength - contained - HeaderLength, recieved = 0; int r = buffer.Offset; while (needed > 0) { r += socket.Receive(buffer.Array, r, needed, System.Net.Sockets.SocketFlags.None); needed -= r; recieved += r; } m_Packet = m_Packet.Concat(buffer).ToArray(); return(recieved); }
/// <summary> /// Calls <see cref="Disconnect"/> and disposes all contained <see cref="RtpClient.TransportContext"/>. /// Stops the raising of any events. /// Removes the Logger /// </summary> public override void Dispose() { if (IsDisposed) return; Disconnect(); base.Dispose(); if (false == ShouldDispose) return; //Dispose contexts foreach (TransportContext tc in TransportContexts) tc.Dispose(); //Counters go away with the transportChannels TransportContexts.Clear(); //Remove my handler (should be done when set to null anyway) RtpPacketSent -= new RtpPacketHandler(HandleOutgoingRtpPacket); RtcpPacketSent -= new RtcpPacketHandler(HandleOutgoingRtcpPacket); //Stop raising events RtpPacketSent = null; RtcpPacketSent = null; RtpPacketReceieved = null; RtcpPacketReceieved = null; InterleavedData = null; //Send abort signal Media.Common.Extensions.Thread.ThreadExtensions.TryAbort(ref m_WorkerThread); //Send abort signal to all threads contained. //Delegate AbortDelegate //Media.Common.IThreadReferenceExtensions.AbortAll(this); DisableFeedbackReports(this); //Empty packet buffers m_OutgoingRtpPackets.Clear(); m_OutgoingRtcpPackets.Clear(); //Remove the buffer m_Buffer.Dispose(); m_Buffer = null; //Unset the logger Logger = null; }
internal void ProcessClientBuffer(ClientSession session, int received) { if (session == null || session.IsDisposed || received <= 0) return; try { //Use a segment around the data received which is already in the buffer. using (Common.MemorySegment data = new Common.MemorySegment(session.m_Buffer.Array, session.m_Buffer.Offset, received)) { if (data[0] == RtpClient.BigEndianFrameControl) { if (session.m_RtpClient == null) return; received -= session.m_RtpClient.ProcessFrameData(session.m_Buffer.Array, session.m_Buffer.Offset, received, session.m_RtspSocket); } if (received <= 0) return; //Ensure the message is really Rtsp RtspMessage request = new RtspMessage(data); //Check for validity if (request.MessageType != RtspMessageType.Invalid) { //Process the request when complete if (request.IsComplete) { ProcessRtspRequest(request, session); return; } } //Otherwise if LastRequest is not disposed then attempt completion with the invalid data else if (session.LastRequest != null && false == session.LastRequest.IsDisposed) { //Indicate a CompleteFrom is occuring Media.Common.ILoggingExtensions.Log(Logger, "Session:" + session.Id + " Attempting to complete previous mesage with buffer of " + data.Count + " bytes."); //Attempt to complete it received = session.LastRequest.CompleteFrom(null, data); //If nothing was recieved then do nothing. if (received == 0) { Media.Common.ILoggingExtensions.Log(Logger, "Session:" + session.Id + "Did not use buffer of " + data.Count + " bytes."); //return; } Media.Common.ILoggingExtensions.Log(Logger, "Session:" + session.Id + " used " + received + " of buffer bytes"); //Account for the received data session.m_Receieved += received; //Determine how to process the messge switch (session.LastRequest.MessageType) { case RtspMessageType.Response: case RtspMessageType.Invalid: { //Ensure the session is still connected. session.SendRtspData(Media.Common.MemorySegment.EmptyBytes); return; } case RtspMessageType.Request: { //If the request is complete now then process it if (session.LastRequest.IsComplete) { //Process the request (it may not be complete yet) ProcessRtspRequest(session.LastRequest, session); return; } goto case RtspMessageType.Invalid; } } } //Log the invalid request Media.Common.ILoggingExtensions.Log(Logger, "Received Invalid Message:" + request + " \r\nFor Session:" + session.Id); if (session.LastRequest != null) session.LastRequest.Dispose(); //Store it for now to allow completion. session.LastRequest = request; //Ensure the session is still connected. session.SendRtspData(Media.Common.MemorySegment.EmptyBytes); } } catch(Exception ex) { if (Logger != null) Logger.LogException(ex); } }
/// <summary> /// Creates a RtcpPacket instance from the given parameters by copying data. /// </summary> /// <param name="buffer">The buffer which contains the binary RtcpPacket to decode</param> /// <param name="offset">The offset to start decoding</param> public RtcpPacket(byte[] buffer, int offset, int count, bool shouldDispose = true) { //The instance owns the header ShouldDispose = m_OwnsHeader = shouldDispose; //Create the header Header = new RtcpHeader(buffer, offset); //Determine the amount of bytes in the header and packet int headerLength = RtcpHeader.Length, packetLength = Header.LengthInWordsMinusOne; switch (packetLength) { case ushort.MinValue: { Payload = Common.MemorySegment.Empty; if(false == Padding) return; break; } default: { //Header has another word headerLength = Binary.BytesPerLong; //Packet length is given by the LengthInWordsMinusOne + 1 * 4 packetLength = ((ushort)((packetLength + 1) * 4)); //If there is no data in the payload don't consume it if (packetLength < headerLength) goto case ushort.MinValue; break; } } //ssrc doesn't technically coun't //int nonHeaderBytes = packetLength - headerLength; //Project the octets in the sequence taking the minimum of the octets present and the octets required as indicated by the header. //m_OwnedOctets = buffer.Skip(offset + headerLength).Take(nonHeaderBytes < 0 ? buffer.Length + nonHeaderBytes : nonHeaderBytes).ToArray(); //The Payload property must be assigned otherwise the properties will not function in the instance. //Payload = new Common.MemorySegment(m_OwnedOctets, shouldDispose); Payload = new Common.MemorySegment(buffer, offset + headerLength, count - headerLength, shouldDispose); m_OwnedOctets = Payload.Array; }
public virtual int CompleteFrom(System.Net.Sockets.Socket socket, Common.MemorySegment buffer) { if (IsReadOnly) throw new InvalidOperationException("Cannot modify a RtcpPacket when IsReadOnly is false."); //If the packet is complete then return if (IsDisposed || IsComplete) return 0; //Calulcate the amount of octets remaining in the RtcpPacket including the header int octetsRemaining = ((ushort)(Header.LengthInWordsMinusOne + 1)) * 4 - Length, offset = Payload != null ? Payload.Count : 0; if (octetsRemaining > 0) { //There is not enough room in the array to finish the packet if (Payload.Count < octetsRemaining) { //Allocte the memory for the required data if (m_OwnedOctets == null) m_OwnedOctets = new byte[octetsRemaining]; else m_OwnedOctets = m_OwnedOctets.Concat(new byte[octetsRemaining]).ToArray(); } System.Net.Sockets.SocketError error; int recieved = 0; //Read from the stream, decrementing from octetsRemaining what was read. while (octetsRemaining > 0) { int rec = Media.Common.Extensions.Socket.SocketExtensions.AlignedReceive(m_OwnedOctets, offset, octetsRemaining, socket, out error); offset += rec; octetsRemaining -= rec; recieved += rec; } //Re-allocate the segment around the received data. Payload = new Common.MemorySegment(m_OwnedOctets, 0, m_OwnedOctets.Length); return recieved; } return 0; }
public ClientSession(RtspServer server, Socket rtspSocket, Common.MemorySegment buffer = null) { Id = Guid.NewGuid(); //The RtspSession ID should be set here to prevent this session from accessing another session, //The only problem with this is that is how the nature of TCP may work also... e.g. the client may open and close connections at will in between requests. //This means any TCP connection can technially just as in UDP access another session so long as the SessionID is known. //Agents will attempt to check the EndPoint however if the packet was forged [and successfully transmitted] then the session is obtained through that mechanism... //TCP provides a `stronger` protection against this type of attack (forging) by default where as UDP does not and most large entities are their own provider and thus... m_Server = server; //Assign the socket and remote endPoint, IPPacketInformation provides thus for UDP RtspSocket = rtspSocket; if (m_RtspSocket == null) return; //m_RtspSocket.Blocking = false; //m_RtspSocket.ExclusiveAddressUse = true; //Configure TCP Sockets if (m_RtspSocket.ProtocolType == ProtocolType.Tcp) { m_RtspSocket.NoDelay = true; m_RtspSocket.SendBufferSize = 0; m_RtspSocket.ReceiveBufferSize = 0; Media.Common.Extensions.Socket.SocketExtensions.DisableLinger(m_RtspSocket); //Use expedited data as defined in RFC-1222. This option can be set only once; after it is set, it cannot be turned off. m_RtspSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.Expedited, true); } if (m_RtspSocket.AddressFamily == AddressFamily.InterNetwork) m_RtspSocket.DontFragment = true; //m_RtspSocket.SendTimeout = m_RtspSocket.ReceiveTimeout = (int)(m_Server.RtspClientInactivityTimeout.TotalMilliseconds / 3); m_RtspSocket.SendTimeout = m_RtspSocket.ReceiveTimeout = (int)RtspClient.DefaultConnectionTime.TotalMilliseconds; //Create a buffer using the size of the largest message possible without a Content-Length header. //This helps to ensure that partial messages are not recieved by the server from a client if possible (should eventually allow much smaller) if (buffer == null) m_Buffer = new Common.MemorySegment(RtspMessage.MaximumLength); else m_Buffer = buffer; //Start receiving data StartReceive(); }
/// <summary> /// Parses the data in the buffer for valid Rtcp and Rtcp packet instances. /// </summary> /// <param name="memory">The memory to parse</param> /// <param name="from">The socket which received the data into memory and may be used for packet completion.</param> protected internal virtual void ParseAndCompleteData(Common.MemorySegment memory, bool parseRtcp = true, bool parseRtp = true, int? remaining = null) { if (memory == null || memory.IsDisposed || memory.Count == 0) return; //handle demultiplex scenarios e.g. RFC5761 if (parseRtcp == parseRtp && memory.Count > RFC3550.CommonHeaderBits.Size) { //Double Negitive, Demux based on PayloadType? RFC5761? //Distinguishable RTP and RTCP Packets //http://tools.ietf.org/search/rfc5761#section-4 //Observation 1) Rtp packets can only have a PayloadType from 64-95 //However Rtcp Packets may also use PayloadTypes 72- 76.. (Reduced size...) //Observation 2) Rtcp Packets defined in RFC3550 Start at 200 (SR -> Goodbye) 204, // 209 - 223 is cited in the above as well as below //RTCP packet types in the ranges 1-191 and 224-254 SHOULD only be used when other values have been exhausted. using (Media.RFC3550.CommonHeaderBits header = new Media.RFC3550.CommonHeaderBits(memory)) { //Just use the payload type to avoid confusion, payload types for Rtcp and Rtp cannot and should not overlap parseRtcp = !(parseRtp = GetContextByPayloadType(header.RtpPayloadType) != null); //Could also lookup the ssrc } } //Cache start, count and index int offset = memory.Offset, count = memory.Count, index = 0, //Calulcate remaining mRemaining = remaining ?? count - index; //If there is nothing left to parse then return if (count <= 0) return; //If rtcp should be parsed if (parseRtcp && mRemaining >= RtcpHeader.Length) { //Copy valid RtcpPackets out of the buffer now, if any packet is not complete it will be completed only if required. foreach (RtcpPacket rtcp in RtcpPacket.GetPackets(memory.Array, offset + index, mRemaining)) { //Raise an event for each packet. //OnRtcpPacketReceieved(rtcp); HandleIncomingRtcpPacket(this, rtcp); //Move the offset the length of the packet parsed index += rtcp.Length; mRemaining -= rtcp.Length; } } //If rtp is parsed if (parseRtp && mRemaining >= RtpHeader.Length) { using (var subMemory = new Common.MemorySegment(memory.Array, offset + index, mRemaining)) { using (RtpPacket rtp = new RtpPacket(subMemory)) { Console.WriteLine(rtp.SequenceNumber+"==================="); //Raise the event HandleIncomingRtpPacket(this, rtp); //Move the index past the length of the packet index += rtp.Length; //Calculate the amount of octets remaining in the segment. mRemaining -= rtp.Length; } } } //If not all data was consumed if (mRemaining > 0) { Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@ParseAndCompleteData - Remaining= " + mRemaining); //Only handle when not in TCP? //OnInterleavedData(memory.Array, offset + index, mRemaining); } return; }
/// <summary> /// Recieves data on a given socket and endpoint /// </summary> /// <param name="socket">The socket to receive data on</param> /// <returns>The number of bytes recieved</returns> protected internal virtual int ReceiveData(Socket socket, ref EndPoint remote, out SocketError error, bool expectRtp = true, bool expectRtcp = true, MemorySegment buffer = null) { //Nothing bad happened yet. error = SocketError.SocketError; //Ensure the socket can poll if (IsDisposed || m_StopRequested || socket == null || m_Buffer.IsDisposed || remote == null) return 0; bool tcp = socket.ProtocolType == ProtocolType.Tcp; if (buffer == null) buffer = m_Buffer; //Cache the offset at the time of the call int offset = buffer.Offset, received = 0; try { //Determine how much data is 'Available' //int available = socket.ReceiveFrom(m_Buffer.Array, offset, m_Buffer.Count, SocketFlags.Peek, ref remote); error = SocketError.Success; ////If the receive was a success //if (available > 0) //{ received = socket.ReceiveFrom(buffer.Array, offset, buffer.Count, SocketFlags.None, ref remote); //Under TCP use Framing to obtain the length of the packet as well as the context. if (tcp) return ProcessFrameData(buffer.Array, offset, received, socket); //Lookup the context to determine if the packet will fit var context = GetContextBySocket(socket); //If there was a context and packet cannot fit if (context != null && received > context.MaximumPacketSize) { //Log the problem Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@ReceiveData - Cannot fit packet in buffer"); //Determine if there was enough data to determine if the packet was rtp or rtcp and indicate a failed reception //if (received > RFC3550.CommonHeaderBits.Size) //{ // //context.m_FailedRtcpReceptions++; // //context.m_FailedRtpReceptions++; //} //remove the reference context = null; } //Use the data received to parse and complete any recieved packets, should take a parseState using (var memory = new Common.MemorySegment(buffer.Array, offset, received)) ParseAndCompleteData(memory, expectRtcp, expectRtp); //} } catch (SocketException se) { error = (SocketError)se.ErrorCode; return received; } catch { throw; } //Return the amount of bytes received from this operation return received; }
/// <summary> /// Used to handle Tcp framing /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="count"></param> /// <param name="socket"></param> /// <returns></returns> protected internal virtual int ProcessFrameData(byte[] buffer, int offset, int count, Socket socket) { if (count == 0) return 0; //If there is no buffer use our own buffer. if (buffer == null) buffer = m_Buffer.Array; //Determine which TransportContext will receive the data incoming TransportContext relevent = null; //The channel of the data byte frameChannel = 0; //Get the length of the given buffer (Should actually use m_Buffer.Count when using our own buffer) int bufferLength = buffer.Length, //The indicates length of the data frameLength = 0, //The amount of data remaining in the buffer remainingInBuffer = Media.Common.Extensions.Math.MathExtensions.Clamp(count, count, bufferLength - offset), //The amount of data received (which is already equal to what is remaining in the buffer) recievedTotal = remainingInBuffer; //Determine if Rtp or Rtcp is coming in or some other type (could be combined with expectRtcp and expectRtp == false) bool expectRtp = false, expectRtcp = false, incompatible = true, raisedEvent = false; //If anything remains on the socket the value will be calulcated. int remainingOnSocket = 0; //TODO handle receiving when no $ and Channel is presenent... e.g. RFC4571 //Would only be 2 then... //if (GetContextBySocket(socket).MediaDescription.MediaProtocol.StartsWith("TCP", StringComparison.OrdinalIgnoreCase)) //{ // //independent = true; //} int sessionRequired = InterleavedOverhead; //While not disposed and there is data remaining (within the buffer) while (false == IsDisposed && remainingInBuffer > 0 && offset >= m_Buffer.Offset) { //Assume not rtp or rtcp and that the data is compatible with the session expectRtp = expectRtcp = incompatible = false; //If a header can be read if (remainingInBuffer >= sessionRequired) { //Determine if an event was raised each time there was at least the required amount of data. raisedEvent = false; //Parse the frameLength from the given buffer, take changes to the offset through the function. frameLength = ReadApplicationLayerFraming(remainingInBuffer, out frameChannel, out relevent, ref offset, out raisedEvent, buffer); //If a frame was found (Including the null packet) if (frameLength >= 0) { //If there WAS a context if (relevent != null) { //Verify minimum and maximum packet sizes allowed by context. (taking into account the amount of bytes in the ALF) if (frameLength < relevent.MinimumPacketSize + sessionRequired || frameLength > relevent.MaximumPacketSize + sessionRequired) { //mark as incompatible incompatible = true; //ToDo //Make CreateLogString function Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@ProcessFrameData - Irregular Packet of " + frameLength + " for Channel " + frameChannel + " remainingInBuffer=" + remainingInBuffer); //jump goto CheckPacketAttributes; } //TODO Independent framing... (e.g. no $)[ only 4 bytes not 6 ] //If all that remains is the frame header then receive more data. 6 comes from (InterleavedOverhead + CommonHeaderBits.Size) //We need more data to be able to verify the frame. if (remainingInBuffer <= 6) { //Remove the context relevent = null; goto CheckRemainingData; ////Only receive this many more bytes for now. //remainingOnSocket = X - remainingInBuffer; ////Receive the rest of the data indicated by frameLength. (Should probably only receive up to X more bytes then make another receive if needed) //goto GetRemainingData; } //Use CommonHeaderBits on the data after the Interleaved Frame Header using (var common = new Media.RFC3550.CommonHeaderBits(buffer[offset + sessionRequired], buffer[offset + sessionRequired + 1])) { //Check the version... incompatible = common.Version != relevent.Version; //If this is a valid context there must be at least a RtpHeader's worth of data in the buffer. //If this was a RtcpPacket with only 4 bytes it wouldn't have a ssrc and wouldn't be valid to be sent. if (false == incompatible && (frameChannel == relevent.DataChannel && remainingInBuffer <= Rtp.RtpHeader.Length + sessionRequired) || (frameChannel == relevent.ControlChannel && remainingInBuffer <= Rtcp.RtcpHeader.Length + sessionRequired)) { //Remove the context relevent = null; //Mark as incompatible incompatible = true; goto EndUsingHeader; ////Only receive this many more bytes for now. //remainingOnSocket = 16 - remainingInBuffer; ////Receive the rest of the data indicated by frameLength. (Should probably only receive up to 6 more bytes then make another receive if needed) //goto GetRemainingData; } //Perform a set of checks and set weather or not Rtp or Rtcp was expected. if (false == incompatible) { //Determine if the packet is Rtcp by looking at the found channel and the relvent control channel if (frameChannel == relevent.ControlChannel) { //Rtcp if (remainingInBuffer <= sessionRequired + Rtcp.RtcpHeader.Length) { //Remove the context relevent = null; goto CheckRemainingData; } //Store any rtcp length so we can verify its not 0 and then additionally ensure its value is not larger then the frameLength int rtcpLen; //use a rtcp header to extract the information in the packet using (Rtcp.RtcpHeader header = new RtcpHeader(buffer, offset + sessionRequired)) { //Get the length in 'words' (by adding one) //A length of 0 means 1 word //A length of 65535 means only the header (no ssrc [or payload]) ushort lengthInWordsPlusOne = (ushort)(header.LengthInWordsMinusOne + 1); //Convert to bytes rtcpLen = lengthInWordsPlusOne * 4; //Check that the supposed amount of contained words is greater than or equal to the frame length conveyed by the application layer framing //it must also be larger than the buffer incompatible = rtcpLen > frameLength && rtcpLen > bufferLength; //if rtcpLen >= ushort.MaxValue the packet may possibly span multiple segments unless a large buffer is used. if (false == incompatible && //It was not already ruled incomaptible lengthInWordsPlusOne > 0 && //If there is supposed to be SSRC in the packet header.Size > Rtcp.RtcpHeader.Length && //The header ACTUALLY contains enough bytes to have a SSRC false == relevent.InDiscovery)//The remote context knowns the identity of the remote stream { //Determine if Rtcp is expected //Perform another lookup and check compatibility expectRtcp = !(incompatible = (GetContextBySourceId(header.SendersSynchronizationSourceIdentifier)) == null); } } } //May be mixing channels... if (false == expectRtcp) { //Rtp if (remainingInBuffer <= sessionRequired + Rtp.RtpHeader.Length) { //Remove the context relevent = null; goto CheckRemainingData; } //the context by payload type is null is not discovering the identity check the SSRC. if (GetContextByPayloadType(common.RtpPayloadType) == null && false == relevent.InDiscovery) { using (Rtp.RtpHeader header = new RtpHeader(buffer, offset + InterleavedOverhead)) { //The context was obtained by the frameChannel //Use the SSRC to determine where it should be handled. //If there is no context the packet is incompatible expectRtp = !(incompatible = (GetContextBySourceId(header.SynchronizationSourceIdentifier)) == null); //(Could also check SequenceNumber to prevent duplicate packets from being processed.) ////Verify extensions (handled by ValidatePacket) //if (header.Extension) //{ //} } } else incompatible = false; } } EndUsingHeader: ; } } //Log state. //if (relevent == null) Media.Common.ILoggingExtensions.Log(Logger, InternalId + "-ProcessFrameData - No Context for Channel " + frameChannel + " frameLength=" + frameLength + " remainingInBuffer=" + remainingInBuffer); //else Media.Common.ILoggingExtensions.Log(Logger, InternalId + "ProcessFrameData " + frameChannel + " frameLength=" + frameLength + " remainingInBuffer=" + remainingInBuffer); CheckPacketAttributes: if (incompatible) { //If there was a context then incrment for failed receptions if (relevent != null) { if (expectRtp) ++relevent.m_FailedRtpReceptions; if (expectRtcp) ++relevent.m_FailedRtcpReceptions; } Media.Common.ILoggingExtensions.Log(Logger, InternalId + "ProcessFrameData - Incompatible Packet frameLength=" + frameLength + " for Channel " + frameChannel + " remainingInBuffer=" + remainingInBuffer); } //If frameLength was 0 or the frame was larger than we can store then interleave the header for handling if required //incompatible may not be true here. else if (frameLength == 0 || frameLength > bufferLength) { //Could check incompatible to determine if to should move further. //Just because there is no assoicated context on the client does not mean the packet is not useful elsewhere in Transport. //TODO It may be possible to let the event reiever known how much is available here. if (frameLength == 0) { Media.Common.ILoggingExtensions.Log(Logger, InternalId + "ProcessFrameData - Null Packet for Channel " + frameChannel + " remainingInBuffer=" + remainingInBuffer); } else //If there was a context then increment for failed receptions only for large packets { if (expectRtp) ++relevent.m_FailedRtpReceptions; if (expectRtcp) ++relevent.m_FailedRtcpReceptions; if (expectRtp || expectRtcp) Media.Common.ILoggingExtensions.Log(Logger, InternalId + "ProcessFrameData - Large Packet of " + frameLength + " for Channel " + frameChannel + " remainingInBuffer=" + remainingInBuffer); } } else goto CheckRemainingData; //The packet was incompatible or larger than the buffer //Determine how much we can move int toMove = Math.Min(remainingInBuffer, sessionRequired); //TODO It may be possible to let the event reiever known how much is available here. //Indicate what was received if not already done if (false == raisedEvent) OnInterleavedData(buffer, offset, toMove); //Move the offset offset += toMove; //Decrease by the length remainingInBuffer -= toMove; //Do another pass continue; }//else there was a frameLength of -1 this indicates there is not enough bytes for a header. } else//There is not enough data in the buffer as defined by sessionRequired. { //unset the frameLength read frameLength = -1; //unset the context read relevent = null; } //At this point there may be either less sessionRequired or not enough for a complete frame. CheckRemainingData: //See how many more bytes are required from the wire //If the frameLength is less than 0 AND there are less then are sessionRequired remaining in the buffer remainingOnSocket = frameLength < 0 && remainingInBuffer < sessionRequired ? sessionRequired - remainingInBuffer //Receive enough to complete the header : //Otherwise if the frameLength larger then what remains in the buffer allow for the buffer to be filled or nothing else remains. frameLength > remainingInBuffer ? frameLength - remainingInBuffer : 0; //GetRemainingData: //If there is anymore data remaining on the wire if (remainingOnSocket > 0) { //Align the buffer if anything remains on the socket. Array.Copy(buffer, offset, buffer, m_Buffer.Offset, remainingInBuffer); //Set the correct offset either way. offset = m_Buffer.Offset + remainingInBuffer; //Store the error if any SocketError error = SocketError.SocketError; //Get all the remaining data while (false == IsDisposed && remainingOnSocket > 0) { //Recieve from the wire the amount of bytes required (up to the length of the buffer) int recievedFromWire = socket == null ? 0 : Media.Common.Extensions.Socket.SocketExtensions.AlignedReceive(buffer, offset, remainingOnSocket, socket, out error); //Check for an error and then the allowed continue condition if (error != SocketError.Success && error != SocketError.TryAgain) break; //If nothing was recieved try again. if (recievedFromWire <= 0) continue; //Decrease what is remaining from the wire by what was received remainingOnSocket -= recievedFromWire; //Move the offset offset += recievedFromWire; //Increment received recievedTotal += recievedFromWire; //Incrment remaining in buffer for what was recieved. remainingInBuffer += recievedFromWire; } //If a socket error occured remove the context so no parsing occurs if (error != SocketError.Success) { OnInterleavedData(buffer, offset - remainingInBuffer, remainingInBuffer); return recievedTotal; } //Move back to where the frame started offset -= remainingInBuffer; } //If there was data unrelated to a frame if (raisedEvent) { if (relevent == null) { offset += frameLength; remainingInBuffer -= frameLength; } continue; } else if (false == IsDisposed && frameLength > 0) { //Parse the data in the buffer using (var memory = new Common.MemorySegment(buffer, offset + InterleavedOverhead, frameLength - InterleavedOverhead)) ParseAndCompleteData(memory, expectRtcp, expectRtp, memory.Count); //Decrease remaining in buffer remainingInBuffer -= frameLength; //Move the offset offset += frameLength; //Ensure large frames are completely received by receiving the rest of the frame now. (this only occurs for packets being skipped) if (frameLength > bufferLength) { //Remove the context relevent = null; //Determine how much remains remainingOnSocket = frameLength - bufferLength; //If there is anything left if (remainingOnSocket > 0) { //Set the new length of the frame based on the length of the buffer frameLength -= bufferLength; //Set what is remaining remainingInBuffer = 0; //Use all the buffer offset = m_Buffer.Offset; //go to receive it goto CheckRemainingData; } } } } //Handle any data which remains if not already if (false == raisedEvent && remainingInBuffer > 0) { OnInterleavedData(buffer, offset, remainingInBuffer); } //Return the number of bytes recieved return recievedTotal; }
//overload for padd if necessary? /// <summary> /// Copies the given octets to the Payload before any Padding and calls <see cref="SetLengthInWordsMinusOne"/>. /// </summary> /// <param name="octets">The octets to add</param> /// <param name="offset">The offset to start copying</param> /// <param name="count">The amount of bytes to copy</param> protected internal virtual void AddBytesToPayload(IEnumerable<byte> octets, int offset = 0, int count = int.MaxValue, bool setLength = true) { if (IsReadOnly) throw new InvalidOperationException("Can only set the AddBytesToPayload when IsReadOnly is false."); //Build a seqeuence from the existing octets and the data in the ReportBlock //If there are existing owned octets (which may include padding) if (Padding) { //Determine the amount of bytes in the payload int payloadCount = Payload.Count, //Determine the padding octets offset paddingOctets = PaddingOctets, //Determine the amount of octets in the payload payloadOctets = payloadCount - paddingOctets; //The owned octets is a projection of the Payload existing, without the padding combined with the given octets from offset to count and subsequently the paddingOctets after the payload m_OwnedOctets = Enumerable.Concat(Payload.Take(payloadOctets), octets.Skip(offset).Take(count - offset)) .Concat(Payload.Skip(payloadOctets).Take(paddingOctets)).ToArray(); } else if (m_OwnedOctets == null) m_OwnedOctets = octets.Skip(offset).Take(count - offset).ToArray(); else m_OwnedOctets = Enumerable.Concat(m_OwnedOctets, octets.Skip(offset).Take(count - offset)).ToArray(); //Create a pointer to the owned octets. Payload = new Common.MemorySegment(m_OwnedOctets); //Set the length in words minus one in the header if(setLength) SetLengthInWordsMinusOne(); }
/// <summary> /// Assigns the events necessary for operation and creates or assigns memory to use as well as inactivtyTimout. /// </summary> /// <param name="memory">The optional memory segment to use</param> /// <param name="incomingPacketEventsEnabled"><see cref="IncomingPacketEventsEnabled"/></param> /// <param name="frameChangedEventsEnabled"><see cref="FrameChangedEventsEnabled"/></param> public RtpClient(Common.MemorySegment memory = null, bool incomingPacketEventsEnabled = true, bool frameChangedEventsEnabled = true, bool outgoingPacketEvents = true) : this() { if (memory == null) { //Determine a good size based on the MTU (this should cover most applications) //Need an IP or the default IP to ensure the MTU Matches. m_Buffer = new Common.MemorySegment(1500); } else { m_Buffer = memory; if (m_Buffer.Count < RtpHeader.Length) throw new ArgumentOutOfRangeException("memory", "memory.Count must contain enough space for a RtpHeader"); } //RtpPacketReceieved += new RtpPacketHandler(HandleIncomingRtpPacket); //RtcpPacketReceieved += new RtcpPacketHandler(HandleIncomingRtcpPacket); RtpPacketSent += new RtpPacketHandler(HandleOutgoingRtpPacket); RtcpPacketSent += new RtcpPacketHandler(HandleOutgoingRtcpPacket); //InterleavedData += new InterleaveHandler(HandleInterleavedData); //Allow events to be raised HandleIncomingRtpPackets = HandleIncomingRtcpPackets = IncomingRtpPacketEventsEnabled = IncomingRtcpPacketEventsEnabled = incomingPacketEventsEnabled; //Fire events for packets received and Allow events to be raised HandleOutgoingRtpPackets = HandleOutgoingRtcpPackets = OutgoingRtpPacketEventsEnabled = OutgoingRtcpPacketEventsEnabled = outgoingPacketEvents; //Handle frame changes and Allow frame change events to be raised HandleFrameChanges = FrameChangedEventsEnabled = frameChangedEventsEnabled; }
/// <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; }