public void SendMidiMessage(MidiMessage message, long timestamp) { sequenceNumber++; long currentTimeIn100Microseconds = RtpMidiSession.GetCurrentTimestamp(); int rtpTimestamp = ((int)currentTimeIn100Microseconds); RtpHeader rtpHeader = new RtpHeader((byte)2, false, false, (byte)0, false, (byte)97, sequenceNumber, rtpTimestamp, Ssrc); Log.Debug("RtpMidi", "Sending RTP-Header: {}", rtpHeader); bool b = message.Length > 15; MidiCommandHeader midiCommandHeader = new MidiCommandHeader(b, false, false, false, (short)message.Length, rtpHeader); var commandCol = new List <MidiTimestampPair>(); commandCol.Add(new MidiTimestampPair(0, message)); RtpMidiMessage rtpMidiMessage = new RtpMidiMessage(midiCommandHeader, commandCol); try { RtpMidiMessageSender.Send(rtpMidiMessage, RtpMidiServer); } catch (IOException e) { Log.Error("RtpMidi", "Error sending MidiMessage to {}", RtpMidiServer, e); } }
public void TestRtpHeader() { EndianReader reader = new EndianReader(ResourcesProvider.GetBytes("tcp_control_handshake.bin", ResourceType.Nano)); RtpHeader header = new RtpHeader(); header.Deserialize(reader); Assert.NotNull(header); Assert.True(header.Padding); Assert.False(header.Extension); Assert.Equal <int>(0, header.CsrcCount); Assert.False(header.Marker); Assert.Equal <NanoPayloadType>(NanoPayloadType.ControlHandshake, header.PayloadType); Assert.Equal <ushort>(0, header.SequenceNumber); Assert.Equal <uint>(2847619159, header.Timestamp); Assert.Equal <ushort>(0, header.ConnectionId); Assert.Equal <ushort>(0, header.ChannelId); }
public void Handle(byte[] data, RtpMidiServer rtpMidiServer) { DataInputStream dataInputStream = new DataInputStream(new MemoryStream(data)); byte header1 = (byte)dataInputStream.ReadByte(); byte version = (byte)((header1 >> 6) & 0x03); bool paddingFlag = ((header1 >> 5) & 0x01) == 1; bool extensionFlag = ((header1 >> 4) & 0x01) == 1; byte contributingSourceIdentifiersCount = (byte)(header1 & 0x0F); byte header2 = (byte)dataInputStream.ReadByte(); bool markerFlag = ((header2 >> 7) & 0x01) == 1; byte payloadType = (byte)(header2 & 0x7F); if (payloadType != RTP_MIDI) { return; } short sequenceNumber = dataInputStream.ReadShort(); int timestamp = dataInputStream.ReadInt(); int ssrc = dataInputStream.ReadInt(); RtpHeader rtpHeader = new RtpHeader(version, paddingFlag, extensionFlag, contributingSourceIdentifiersCount, markerFlag, payloadType, sequenceNumber, timestamp, ssrc); byte midiCommandHeader1 = (byte)dataInputStream.ReadByte(); bool b = ((midiCommandHeader1 >> 7) & 0x01) == 1; bool j = ((midiCommandHeader1 >> 6) & 0x01) == 1; bool z = ((midiCommandHeader1 >> 5) & 0x01) == 1; bool p = ((midiCommandHeader1 >> 4) & 0x01) == 1; // Header 2 octets short length; if (b) { byte midiCommandHeader2 = (byte)dataInputStream.ReadByte(); length = (short)((midiCommandHeader1 << 8 | midiCommandHeader2) & 0x0FFF); } else { length = (short)(midiCommandHeader1 & 0x0F); } MidiCommandHeader midiCommandHeader = new MidiCommandHeader(b, j, z, p, length, rtpHeader); byte[] midiCommandBuffer = new byte[length]; int midiCommandBytesRead = dataInputStream.Read(midiCommandBuffer); if (((short)midiCommandBytesRead) != length) { return; } List <MidiTimestampPair> messages = new List <MidiTimestampPair>(); try { DataInputStream midiInputStream = new DataInputStream(new MemoryStream(midiCommandBuffer)); messages.AddRange(ReadMidiMessages(midiCommandHeader, midiInputStream)); HandleMessage(new RtpMidiMessage(midiCommandHeader, messages)); } catch (System.IO.IOException e) { Log.Error("RtpMidi", "IOException while processing MIDI message", e); } }
public RtpPacket(RtpHeader header) { Header = header; }
public RtpPacket(RtpPayloadType payloadType) { Header = new RtpHeader(); Header.PayloadType = payloadType; }
public RtpPacket(RtpPayloadType payloadType, ISerializableLE payload) { Header = new RtpHeader(); Header.PayloadType = payloadType; Payload = payload; }
public RtpPacket() { Header = new RtpHeader(); }
/// <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; }
/// <summary> /// Given a packet using the redundant audio format, the expanded rtp packets are derived from the contents. /// </summary> /// <param name="packet"></param> /// <param name="shouldDispose"></param> /// <returns></returns> public static System.Collections.Generic.IEnumerable <RtpPacket> GetPackets(RtpPacket packet, bool shouldDispose = true) { if (Common.IDisposedExtensions.IsNullOrDisposed(packet)) { yield break; } int headerOctets = packet.HeaderOctets, offset = packet.Payload.Offset + headerOctets, startOffset = offset, remaining = packet.Payload.Count - (headerOctets + packet.PaddingOctets), endHeaders = remaining, headersContained = 0; //If there are not enough bytes for the profile header break. if (remaining < Common.Binary.BytesPerInteger) { yield break; } byte toCheck; //Iterare from the offset of the end of the rtp header until the end of data in the payload while (remaining >= Common.Binary.BytesPerInteger) { // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //|F| block PT | timestamp offset | block length | //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ toCheck = packet.Payload.Array[offset]; //Check for (F)irst bit if ((toCheck & 0x80) != 0) { //Store the offset of the last header. endHeaders = offset; //This byte does not belong to the data. --remaining; break; } //Increment for the header contained. ++headersContained; //Decrement for the header read. remaining -= Common.Binary.BytesPerInteger; //Move by 4 bytes beause the size of the header is 4 bytes. offset += Common.Binary.BytesPerInteger; } //Nothing more to return if (remaining < 0) { yield break; } Common.MemorySegment tempPayload; Rtp.RtpHeader tempHeader; RtpPacket tempResult; int tempPayloadType, tempTimestamp, tempBlockLen; bool marker = packet.Marker; //Start at the offset in bits of the end of header. if (headersContained > 0) { for (int headOffset = startOffset, i = 0, bitOffset = Common.Binary.BytesToBits(ref headOffset); i < headersContained; ++i) { //Read the payloadType out of the header tempPayloadType = (int)Common.Binary.ReadBitsMSB(packet.Payload.Array, Common.Binary.BytesToBits(ref headOffset) + 1, 7); //Read the timestamp offset from the header tempTimestamp = (int)Common.Binary.ReadBitsMSB(packet.Payload.Array, ref bitOffset, 10); //Read the blockLength from the header tempBlockLen = (int)Common.Binary.ReadBitsMSB(packet.Payload.Array, ref bitOffset, 14); //If there are less bytes in the payload than remain in the block stop //if (remaining < tempBlockLen) break; //Get the payload Common.MemorySegment payload = new Common.MemorySegment(packet.Payload.Array, Common.Binary.BitsToBytes(ref bitOffset), tempBlockLen, shouldDispose); //Create the header Rtp.RtpHeader header = new RtpHeader(packet.Version, false, false, marker, tempPayloadType, 0, packet.SynchronizationSourceIdentifier, packet.SequenceNumber, packet.Timestamp + tempTimestamp, shouldDispose); //Create the packet RtpPacket result = new RtpPacket(header, payload, shouldDispose); //Return the packet yield return(result); //Move the offset bitOffset += Common.Binary.BytesToBits(ref tempBlockLen); //Remove the blockLength from the count remaining -= tempBlockLen; } } //If there is anymore data it's values are defined in the header of the given packet. //Read the payloadType out of the headers area (1 bit after the end of headers) 7 bits in size tempPayloadType = (int)Common.Binary.ReadBitsMSB(packet.Payload.Array, Common.Binary.BytesToBits(ref startOffset) + 1, 7); //Get the payload of the temp packet, the blockLen is given by the count in this packet minus the tempPayload = new Common.MemorySegment(packet.Payload.Array, endHeaders, remaining, shouldDispose); //Create the header tempHeader = new RtpHeader(packet.Version, false, false, marker, tempPayloadType, 0, packet.SynchronizationSourceIdentifier, packet.SequenceNumber, packet.Timestamp, shouldDispose); //Create the packet tempResult = new RtpPacket(tempHeader, tempPayload, shouldDispose); //Return the packet yield return(tempResult); }