public void RTCPHeaderRoundTripTest() { Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); RTCPPacket src = new RTCPPacket(12, 122, 561, 6756, 56434); byte[] reports = new byte[84]; byte[] packetBuffer = src.GetBytes(reports); RTCPPacket dst = new RTCPPacket(packetBuffer); Console.WriteLine("SenderSyncSource: " + src.SenderSyncSource + ", " + dst.SenderSyncSource); Console.WriteLine("NTPTimestamp: " + src.NTPTimestamp + ", " + dst.NTPTimestamp); Console.WriteLine("RTPTimestamp: " + src.RTPTimestamp + ", " + dst.RTPTimestamp); Console.WriteLine("SenderPacketCount: " + src.SenderPacketCount + ", " + dst.SenderPacketCount); Console.WriteLine("SenderOctetCount: " + src.SenderOctetCount + ", " + dst.SenderOctetCount); Console.WriteLine("Reports Length: " + src.Reports.Length + ", " + dst.Reports.Length); //Console.WriteLine("Raw Header: " + System.Text.Encoding.ASCII.GetString(headerBuffer, 0, headerBuffer.Length)); Assert.IsTrue(src.SenderSyncSource == dst.SenderSyncSource, "SenderSyncSource was mismatched."); Assert.IsTrue(src.NTPTimestamp == dst.NTPTimestamp, "NTPTimestamp was mismatched."); Assert.IsTrue(src.RTPTimestamp == dst.RTPTimestamp, "RTPTimestamp was mismatched."); Assert.IsTrue(src.SenderPacketCount == dst.SenderPacketCount, "SenderPacketCount was mismatched."); Assert.IsTrue(src.SenderOctetCount == dst.SenderOctetCount, "SenderOctetCount was mismatched."); Assert.IsTrue(src.Reports.Length == dst.Reports.Length, "Reports length was mismatched."); }
/// <summary> /// Sends an RTCP report to the remote agent. /// </summary> /// <param name="rtcpReport"></param> public void SendRTCPReport(RTCPReportTypesEnum reportType, byte[] reportData) { try { RTCPPacket rtcpPacket = new RTCPPacket(0, 0, 0, 0, 0); RTCPReportPacket rtcpReportPacket = new RTCPReportPacket(reportType, reportData); byte[] rtcpReportPacketBytes = rtcpReportPacket.GetBytes(); byte[] rtcpReportBytes = rtcpPacket.GetBytes(rtcpReportPacketBytes); m_udpListener.Send(rtcpReportBytes, rtcpReportBytes.Length, m_streamEndPoint); } catch (Exception excp) { logger.Error("Exception SendRTCPReport. " + excp.Message); } }
public void GetRTCPPacketTest() { Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); RTCPPacket rtcpPacket = new RTCPPacket(1, 1, 1, 1, 1); byte[] reports = new byte[84]; byte[] packetBuffer = rtcpPacket.GetBytes(reports); int byteNum = 1; foreach (byte packetByte in packetBuffer) { Console.WriteLine(byteNum + ": " + packetByte.ToString("x")); byteNum++; } }
private void RTCPReceive() { Thread.CurrentThread.Name = "rtpchanrecv-" + _rtpPort; byte[] buffer = new byte[1024]; DateTime packetTimestamp = DateTime.Now; try { EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); while (IsRunning) { if (_controlSocket == null || _controlSocket.Available == 0) { Thread.Sleep(50); continue; } int bytesRead = _controlSocket.ReceiveFrom(buffer, ref remoteEndPoint); if (bytesRead > 0) { _rtcpTimestamp = DateTimeToNptTimestamp90K(DateTime.Now); RTCPPacket senderReport = new RTCPPacket(_syncSource, DateTimeToNptTimestamp(packetTimestamp), _rtcpTimestamp, 0, 0); byte[] rtcpPacket = senderReport.GetBytes(); _controlSocket.SendTo(rtcpPacket, 0, rtcpPacket.Length, SocketFlags.None, remoteEndPoint); } Thread.Sleep(5); } } catch (SocketException) { } catch (Exception ex) { logger.Error("Exception RTCPChannel.ControlSocketReceive. " + ex); } }
private void Listen() { try { UdpClient udpSvr = m_udpListener; if (udpSvr == null) { logger.Error("The UDP server was not correctly initialised in the RTP sink when attempting to start the listener, the RTP stream has not been intialised."); return; } else { logger.Debug("RTP Listener now listening on " + m_localEndPoint.Address + ":" + m_localEndPoint.Port + "."); } IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); byte[] rcvdBytes = null; m_startRTPReceiveTime = DateTime.MinValue; m_lastRTPReceivedTime = DateTime.MinValue; DateTime previousRTPReceiveTime = DateTime.MinValue; uint previousTimestamp = 0; UInt16 sequenceNumber = 0; UInt16 previousSeqNum = 0; uint senderSendSpacing = 0; uint lastSenderSendSpacing = 0; while (!StopListening) { rcvdBytes = null; try { rcvdBytes = udpSvr.Receive(ref remoteEndPoint); } catch { //logger.Warn("Remote socket closed on receive. Last RTP received " + m_lastRTPReceivedTime.ToString("dd MMM yyyy HH:mm:ss") + ", last RTP successfull send " + m_lastRTPSentTime.ToString("dd MMM yyyy HH:mm:ss") + "."); } if (rcvdBytes != null && rcvdBytes.Length > 0) { // Check whether this is an RTCP report. UInt16 firstWord = BitConverter.ToUInt16(rcvdBytes, 0); if (BitConverter.IsLittleEndian) { firstWord = NetConvert.DoReverseEndian(firstWord); } ushort packetType = 0; if (BitConverter.IsLittleEndian) { packetType = Convert.ToUInt16(firstWord & 0x00ff); } if (packetType == RTCPHeader.RTCP_PACKET_TYPE) { logger.Debug("RTP Listener received remote RTCP report from " + remoteEndPoint + "."); try { RTCPPacket rtcpPacket = new RTCPPacket(rcvdBytes); RTCPReportPacket rtcpReportPacket = new RTCPReportPacket(rtcpPacket.Reports); if (RTCPReportReceived != null) { RTCPReportReceived(this, rtcpReportPacket); } } catch (Exception rtcpExcp) { logger.Error("Exception processing remote RTCP report. " + rtcpExcp.Message); } continue; } // Channel statistics. DateTime rtpReceiveTime = DateTime.Now; if (m_startRTPReceiveTime == DateTime.MinValue) { m_startRTPReceiveTime = rtpReceiveTime; //m_sampleStartTime = rtpReceiveTime; } previousRTPReceiveTime = new DateTime(m_lastRTPReceivedTime.Ticks); m_lastRTPReceivedTime = rtpReceiveTime; m_packetsReceived++; m_bytesReceived += rcvdBytes.Length; previousSeqNum = sequenceNumber; // This stops the thread running the ListenerTimeout method from deciding the strema has recieved no RTP and therefore should be shutdown. m_lastPacketReceived.Set(); // Let whoever has subscribed that an RTP packet has been received. if (DataReceived != null) { try { DataReceived(m_streamId, rcvdBytes, remoteEndPoint); } catch (Exception excp) { logger.Error("Exception RTPSink DataReceived. " + excp.Message); } } if (m_packetsReceived % 500 == 0) { logger.Debug("Total packets received from " + remoteEndPoint.ToString() + " " + m_packetsReceived + ", bytes " + NumberFormatter.ToSIByteFormat(m_bytesReceived, 2) + "."); } try { RTPPacket rtpPacket = new RTPPacket(rcvdBytes); uint syncSource = rtpPacket.Header.SyncSource; uint timestamp = rtpPacket.Header.Timestamp; sequenceNumber = rtpPacket.Header.SequenceNumber; //logger.Debug("seqno=" + rtpPacket.Header.SequenceNumber + ", timestamp=" + timestamp); if (previousRTPReceiveTime != DateTime.MinValue) { //uint senderSendSpacing = rtpPacket.Header.Timestamp - previousTimestamp; // Need to cope with cases where the timestamp has looped, if this timestamp is < last timesatmp and there is a large difference in them then it's because the timestamp counter has looped. lastSenderSendSpacing = senderSendSpacing; senderSendSpacing = (Math.Abs(timestamp - previousTimestamp) > (uint.MaxValue / 2)) ? timestamp + uint.MaxValue - previousTimestamp : timestamp - previousTimestamp; if (previousTimestamp > timestamp) { logger.Error("BUG: Listener previous timestamp (" + previousTimestamp + ") > timestamp (" + timestamp + "), last seq num=" + previousSeqNum + ", seqnum=" + sequenceNumber + "."); // Cover for this bug until it's nailed down. senderSendSpacing = lastSenderSendSpacing; } double senderSpacingMilliseconds = (double)senderSendSpacing / (double)TIMESTAMP_FACTOR; double interarrivalReceiveTime = m_lastRTPReceivedTime.Subtract(previousRTPReceiveTime).TotalMilliseconds; #region RTCP reporting. if (m_rtcpSampler == null) { //resultsLogger.Info("First Packet: " + rtpPacket.Header.SequenceNumber + "," + m_arrivalTime.ToString("HH:mm:fff")); m_rtcpSampler = new RTCPReportSampler(m_streamId, syncSource, remoteEndPoint, rtpPacket.Header.SequenceNumber, m_lastRTPReceivedTime, rcvdBytes.Length); m_rtcpSampler.RTCPReportReady += new RTCPSampleReadyDelegate(m_rtcpSampler_RTCPReportReady); m_rtcpSampler.StartSampling(); } else { //m_receiverReports[syncSource].RecordRTPReceive(rtpPacket.Header.SequenceNumber, sendTime, rtpReceiveTime, rcvdBytes.Length); // Transit time is calculated by knowing that the sender sent a packet at a certain time after the last send and the receiver received a pakcet a certain time after the last receive. // The difference in these two times is the jitter present. The transit time can change with each transimission and as this methid relies on two sends two packet // arrivals to calculate the transit time it's not going to be perfect (you'd need synchronised NTP clocks at each end to be able to be accurate). // However if used tor an average calculation it should be pretty close. //double transitTime = Math.Abs(interarrivalReceiveTime - senderSpacingMilliseconds); uint jitter = (interarrivalReceiveTime - senderSpacingMilliseconds > 0) ? Convert.ToUInt32(interarrivalReceiveTime - senderSpacingMilliseconds) : 0; if (jitter > 75) { logger.Debug("seqno=" + rtpPacket.Header.SequenceNumber + ", timestmap=" + timestamp + ", ts-prev=" + previousTimestamp + ", receive spacing=" + interarrivalReceiveTime + ", send spacing=" + senderSpacingMilliseconds + ", jitter=" + jitter); } else { //logger.Debug("seqno=" + rtpPacket.Header.SequenceNumber + ", receive spacing=" + interarrivalReceiveTime + ", timestamp=" + timestamp + ", transit time=" + transitTime); } m_rtcpSampler.RecordRTPReceive(m_lastRTPReceivedTime, rtpPacket.Header.SequenceNumber, rcvdBytes.Length, jitter); } #endregion } else { logger.Debug("RTPSink Listen SyncSource=" + rtpPacket.Header.SyncSource + "."); } previousTimestamp = timestamp; } catch (Exception excp) { logger.Error("Received data was not a valid RTP packet. " + excp.Message); } #region Switching endpoint if required to cope with NAT. // If a packet is recieved from an endpoint that wasn't expected treat the stream as being NATted and switch the endpoint to the socket on the NAT server. try { if (m_streamEndPoint != null && m_streamEndPoint.Address != null && remoteEndPoint != null && remoteEndPoint.Address != null && (m_streamEndPoint.Address.ToString() != remoteEndPoint.Address.ToString() || m_streamEndPoint.Port != remoteEndPoint.Port)) { logger.Debug("Expecting RTP on " + IPSocket.GetSocketString(m_streamEndPoint) + " but received on " + IPSocket.GetSocketString(remoteEndPoint) + ", now sending to " + IPSocket.GetSocketString(remoteEndPoint) + "."); m_streamEndPoint = remoteEndPoint; if (RemoteEndPointChanged != null) { try { RemoteEndPointChanged(m_streamId, remoteEndPoint); } catch (Exception changeExcp) { logger.Error("Exception RTPListener Changing Remote EndPoint. " + changeExcp.Message); } } } } catch (Exception setSendExcp) { logger.Error("Exception RTPListener setting SendTo Socket. " + setSendExcp.Message); } #endregion } else if (!StopListening) // Empty packet was received possibly indicating connection closure so check for timeout. { double noRTPRcvdDuration = (m_lastRTPReceivedTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPReceivedTime).TotalSeconds : 0; double noRTPSentDuration = (m_lastRTPSentTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPSentTime).TotalSeconds : 0; //logger.Warn("Remote socket closed on receive on " + m_localEndPoint.Address.ToString() + ":" + + m_localEndPoint.Port + ", reinitialising. No rtp for " + noRTPRcvdDuration + "s. last rtp " + m_lastRTPReceivedTime.ToString("dd MMM yyyy HH:mm:ss") + "."); // If this check is not done then the stream will never time out if it doesn't receive the first packet. if (m_lastRTPReceivedTime == DateTime.MinValue) { m_lastRTPReceivedTime = DateTime.Now; } remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); if ((noRTPRcvdDuration > NO_RTP_TIMEOUT || noRTPSentDuration > NO_RTP_TIMEOUT) && StopIfNoData) { logger.Warn("Disconnecting RTP listener on " + m_localEndPoint.ToString() + " due to not being able to send or receive any RTP for " + NO_RTP_TIMEOUT + "s."); Shutdown(); } } } } catch (Exception excp) { logger.Error("Exception Listen RTPSink: " + excp.Message); } finally { #region Shut down socket. Shutdown(); if (ListenerClosed != null) { try { ListenerClosed(m_streamId, m_callDescriptorId); } catch (Exception excp) { logger.Error("Exception RTPSink ListenerClosed. " + excp.Message); } } #endregion } }