/// <summary> /// Constructor for payload feedback reports that do not require any additional feedback control /// indication parameters (e.g. Picture Loss Indication reports). /// </summary> /// <param name="feedbackMessageType">The payload specific feedback type.</param> public RTCPFeedback(uint senderSsrc, uint mediaSsrc, PSFBFeedbackTypesEnum feedbackMessageType) { Header = new RTCPHeader(feedbackMessageType); SenderSSRC = senderSsrc; MediaSSRC = mediaSsrc; SENDER_PAYLOAD_SIZE = 8; }
/// <summary> /// Create a new RTCP SDES item from a serialised byte array. /// </summary> /// <param name="packet">The byte array holding the SDES report.</param> public RTCPSDesReport(byte[] packet) { if (packet.Length < MIN_PACKET_SIZE) { throw new ApplicationException("The packet did not contain the minimum number of bytes for an RTCP SDES packet."); } else if (packet[8] != CNAME_ID) { throw new ApplicationException("The RTCP report packet did not have the requried CNAME type field set correctly."); } Header = new RTCPHeader(packet); if (BitConverter.IsLittleEndian) { SSRC = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 4)); } else { SSRC = BitConverter.ToUInt32(packet, 4); } int cnameLength = packet[9]; CNAME = Encoding.UTF8.GetString(packet, 10, cnameLength); }
/// <summary> /// Create a new RTCP Sender Report from a serialised byte array. /// </summary> /// <param name="packet">The byte array holding the serialised sender report.</param> public RTCPSenderReport(byte[] packet) { if (packet.Length < MIN_PACKET_SIZE) { throw new ApplicationException("The packet did not contain the minimum number of bytes for an RTCPSenderReport packet."); } Header = new RTCPHeader(packet); ReceptionReports = new List <ReceptionReportSample>(); if (BitConverter.IsLittleEndian) { SSRC = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 4)); NtpTimestamp = NetConvert.DoReverseEndian(BitConverter.ToUInt64(packet, 8)); RtpTimestamp = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 16)); PacketCount = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 20)); OctetCount = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 24)); } else { SSRC = BitConverter.ToUInt32(packet, 4); NtpTimestamp = BitConverter.ToUInt64(packet, 8); RtpTimestamp = BitConverter.ToUInt32(packet, 16); PacketCount = BitConverter.ToUInt32(packet, 20); OctetCount = BitConverter.ToUInt32(packet, 24); } int rrIndex = 28; for (int i = 0; i < Header.ReceptionReportCount; i++) { var rr = new ReceptionReportSample(packet.Skip(rrIndex + i * ReceptionReportSample.PAYLOAD_SIZE).ToArray()); ReceptionReports.Add(rr); } }
/// <summary> /// Create a new RTCP Goodbye packet from a serialised byte array. /// </summary> /// <param name="packet">The byte array holding the Goodbye packet.</param> public RTCPBye(byte[] packet) { if (packet.Length < MIN_PACKET_SIZE) { throw new ApplicationException( "The packet did not contain the minimum number of bytes for an RTCP Goodbye packet."); } Header = new RTCPHeader(packet); if (BitConverter.IsLittleEndian) { SSRC = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 4)); } else { SSRC = BitConverter.ToUInt32(packet, 4); } if (packet.Length > MIN_PACKET_SIZE) { int reasonLength = packet[8]; if (packet.Length - MIN_PACKET_SIZE - 1 >= reasonLength) { Reason = Encoding.UTF8.GetString(packet, 9, reasonLength); } } }
public uint FCI; // Feedback Control Information (FCI) public RTCPFeedback(uint ssrc, RTCPFeedbackTypesEnum feedbackMessageType, ushort sequenceNo, ushort bitMask) { Header = new RTCPHeader(feedbackMessageType); SENDER_PAYLOAD_SIZE = 12; MIN_PACKET_SIZE = RTCPHeader.HEADER_BYTES_LENGTH + SENDER_PAYLOAD_SIZE; SenderSSRC = ssrc; PID = sequenceNo; BLP = bitMask; }
public RTCPPacket(uint senderSyncSource, ulong ntpTimestamp, uint rtpTimestamp, uint senderPacketCount, uint senderOctetCount) { Header = new RTCPHeader(); SenderSyncSource = senderSyncSource; NTPTimestamp = ntpTimestamp; RTPTimestamp = rtpTimestamp; SenderPacketCount = senderPacketCount; SenderOctetCount = senderOctetCount; }
public RTCPSenderReport(uint ssrc, ulong ntpTimestamp, uint rtpTimestamp, uint packetCount, uint octetCount, List <ReceptionReportSample> receptionReports) { Header = new RTCPHeader(RTCPReportTypesEnum.SR, (receptionReports != null) ? receptionReports.Count : 0); SSRC = ssrc; NtpTimestamp = ntpTimestamp; RtpTimestamp = rtpTimestamp; PacketCount = packetCount; OctetCount = octetCount; ReceptionReports = receptionReports; }
/// <summary> /// Creates a new RTCP Reception Report payload. /// </summary> /// <param name="ssrc">The synchronisation source of the RTP packet being sent. Can be zero /// if there are none being sent.</param> /// <param name="receptionReports">A list of the reception reports to include. Can be empty.</param> public RTCPReceiverReport(uint ssrc, List <ReceptionReportSample> receptionReports) { if (receptionReports == null || receptionReports.Count == 0) { throw new ArgumentException("At least one reception report must be included for an RTCP Receiver Report."); } Header = new RTCPHeader(RTCPReportTypesEnum.RR, receptionReports.Count); SSRC = ssrc; ReceptionReports = receptionReports; }
public RTCPSenderReport(uint ssrc, ulong ntpTimestamp, uint rtpTimestamp, uint packetCount, uint octetCount, List <ReceptionReportSample> receptionReports) { //if(receptionReports == null || receptionReports.Count == 0) //{ // throw new ArgumentException("At least one reception report must be included for an RTCP Sender Report."); //} Header = new RTCPHeader(RTCPReportTypesEnum.SR, (receptionReports != null) ? receptionReports.Count : 0); SSRC = ssrc; NtpTimestamp = ntpTimestamp; RtpTimestamp = rtpTimestamp; PacketCount = packetCount; OctetCount = octetCount; ReceptionReports = receptionReports; }
public void GetRTCPHeaderTest() { Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); RTCPHeader rtcpHeader = new RTCPHeader(); byte[] headerBuffer = rtcpHeader.GetHeader(0, 0); int byteNum = 1; foreach (byte headerByte in headerBuffer) { Console.WriteLine(byteNum + ": " + headerByte.ToString("x")); byteNum++; } }
/// <summary> /// Creates a new RTCP Bye payload. /// </summary> /// <param name="ssrc">The synchronisation source of the RTP stream being closed.</param> /// <param name="reason">Optional reason for closing. Maximum length is 255 bytes /// (note bytes not characters).</param> public RTCPBye(uint ssrc, string reason) { Header = new RTCPHeader(RTCPReportTypesEnum.BYE, 1); SSRC = ssrc; if (reason != null) { Reason = (reason.Length > MAX_REASON_BYTES) ? reason.Substring(0, MAX_REASON_BYTES) : reason; // Need to take account of multi-byte characters. while (Encoding.UTF8.GetBytes(Reason).Length > MAX_REASON_BYTES) { Reason = Reason.Substring(0, Reason.Length - 1); } } }
/// <summary> /// Create a new RTCP Report from a serialised byte array. /// </summary> /// <param name="packet">The byte array holding the serialised feedback report.</param> public RTCPFeedback(byte[] packet) { Header = new RTCPHeader(packet); if (BitConverter.IsLittleEndian) { SenderSSRC = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 4)); MediaSSRC = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 8)); } else { SenderSSRC = BitConverter.ToUInt32(packet, 4); MediaSSRC = BitConverter.ToUInt32(packet, 8); } // TODO: Depending on the report type additional parameters will need to be deserialised. }
/// <summary> /// Creates a new RTCP SDES payload that can be included in an RTCP packet. /// </summary> /// <param name="ssrc">The synchronisation source of the SDES.</param> /// <param name="cname">Canonical End-Point Identifier SDES item. This should be a /// unique string common to all RTP streams in use by the application. Maximum /// length is 255 bytes (note bytes not characters).</param> public RTCPSDesReport(uint ssrc, string cname) { if (String.IsNullOrEmpty(cname)) { throw new ArgumentNullException("cname"); } Header = new RTCPHeader(RTCPReportTypesEnum.SDES, 1); SSRC = ssrc; CNAME = (cname.Length > MAX_CNAME_BYTES) ? cname.Substring(0, MAX_CNAME_BYTES) : cname; // Need to take account of multi-byte characters. while (Encoding.UTF8.GetBytes(CNAME).Length > MAX_CNAME_BYTES) { CNAME = CNAME.Substring(0, CNAME.Length - 1); } }
public void RTCPHeaderRoundTripTest() { Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); RTCPHeader src = new RTCPHeader(); byte[] headerBuffer = src.GetHeader(17, 54443); RTCPHeader dst = new RTCPHeader(headerBuffer); Console.WriteLine("Version: " + src.Version + ", " + dst.Version); Console.WriteLine("PaddingFlag: " + src.PaddingFlag + ", " + dst.PaddingFlag); Console.WriteLine("ReceptionReportCount: " + src.ReceptionReportCount + ", " + dst.ReceptionReportCount); Console.WriteLine("PacketType: " + src.PacketType + ", " + dst.PacketType); Console.WriteLine("Length: " + src.Length + ", " + dst.Length); //Console.WriteLine("Raw Header: " + System.Text.Encoding.ASCII.GetString(headerBuffer, 0, headerBuffer.Length)); Assert.IsTrue(src.Version == dst.Version, "Version was mismatched."); Assert.IsTrue(src.PaddingFlag == dst.PaddingFlag, "PaddingFlag was mismatched."); Assert.IsTrue(src.ReceptionReportCount == dst.ReceptionReportCount, "ReceptionReportCount was mismatched."); Assert.IsTrue(src.PacketType == dst.PacketType, "PacketType was mismatched."); Assert.IsTrue(src.Length == dst.Length, "Length was mismatched."); }
public RTCPPacket(byte[] packet) { Header = new RTCPHeader(packet); if (BitConverter.IsLittleEndian) { SenderSyncSource = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 4)); NTPTimestamp = NetConvert.DoReverseEndian(BitConverter.ToUInt64(packet, 8)); RTPTimestamp = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 16)); SenderPacketCount = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 20)); SenderOctetCount = NetConvert.DoReverseEndian(BitConverter.ToUInt32(packet, 24)); } else { SenderSyncSource = BitConverter.ToUInt32(packet, 4); NTPTimestamp = BitConverter.ToUInt64(packet, 8); RTPTimestamp = BitConverter.ToUInt32(packet, 16); SenderPacketCount = BitConverter.ToUInt32(packet, 20); SenderOctetCount = BitConverter.ToUInt32(packet, 24); } Reports = new byte[packet.Length - RTCPHeader.HEADER_BYTES_LENGTH - SENDERINFO_BYTES_LENGTH]; Buffer.BlockCopy(packet, RTCPHeader.HEADER_BYTES_LENGTH + SENDERINFO_BYTES_LENGTH, Reports, 0, Reports.Length); }
/// <summary> /// Creates a new RTCP compound packet from a serialised buffer. /// </summary> /// <param name="packet">The serialised RTCP compound packet to parse.</param> public RTCPCompoundPacket(byte[] packet) { int offset = 0; while (offset < packet.Length) { if (packet.Length - offset < RTCPHeader.HEADER_BYTES_LENGTH) { // Not enough bytes left for a RTCP header. break; } else { var buffer = packet.Skip(offset).ToArray(); // The payload type field is the second byte in the RTCP header. byte packetTypeID = buffer[1]; switch (packetTypeID) { case (byte)RTCPReportTypesEnum.SR: SenderReport = new RTCPSenderReport(buffer); int srLength = (SenderReport != null) ? SenderReport.GetBytes().Length : Int32.MaxValue; offset += srLength; break; case (byte)RTCPReportTypesEnum.RR: ReceiverReport = new RTCPReceiverReport(buffer); int rrLength = (ReceiverReport != null) ? ReceiverReport.GetBytes().Length : Int32.MaxValue; offset += rrLength; break; case (byte)RTCPReportTypesEnum.SDES: SDesReport = new RTCPSDesReport(buffer); int sdesLength = (SDesReport != null) ? SDesReport.GetBytes().Length : Int32.MaxValue; offset += sdesLength; break; case (byte)RTCPReportTypesEnum.BYE: Bye = new RTCPBye(buffer); int byeLength = (Bye != null) ? Bye.GetBytes().Length : Int32.MaxValue; offset += byeLength; break; case (byte)RTCPReportTypesEnum.RTPFB: // TODO: Interpret Generic RTP feedback reports. var rtpfbHeader = new RTCPHeader(buffer); offset += rtpfbHeader.Length * 4 + 4; break; case (byte)RTCPReportTypesEnum.PSFB: // TODO: Interpret Payload specific feedback reports. var psfbHeader = new RTCPHeader(buffer); offset += psfbHeader.Length * 4 + 4; break; default: logger.LogWarning($"RTCPCompoundPacket did not recognise packet type ID {packetTypeID}."); offset = Int32.MaxValue; logger.LogWarning(packet.HexStr()); break; } } } }
/// <summary> /// Creates a new RTCP Reception Report payload. /// </summary> /// <param name="ssrc">The synchronisation source of the RTP packet being sent. Can be zero /// if there are none being sent.</param> /// <param name="receptionReports">A list of the reception reports to include. Can be empty.</param> public RTCPReceiverReport(uint ssrc, List <ReceptionReportSample> receptionReports) { Header = new RTCPHeader(RTCPReportTypesEnum.RR, receptionReports != null ? receptionReports.Count : 0); SSRC = ssrc; ReceptionReports = receptionReports; }