/// <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); } }
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 } }