Esempio n. 1
0
        /// <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.Logger.Error("Exception SendRTCPReport. ->" + excp.Message);
            }
        }
Esempio n. 2
0
        private void Listen()
        {
            try
            {
                UdpClient udpSvr = m_udpListener;

                if (udpSvr == null)
                {
                    Logger.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.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;
                ushort   sequenceNumber         = 0;
                ushort   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.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.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.Logger.Error("Exception RTPSink DataReceived. ->" + excp.Message);
                            }
                        }

                        if (m_packetsReceived % 500 == 0)
                        {
                            Logger.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.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.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.Logger.Debug("RTPSink Listen SyncSource=" + rtpPacket.Header.SyncSource + ".");
                            }

                            previousTimestamp = timestamp;
                        }
                        catch (Exception excp)
                        {
                            Logger.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.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.Logger.Error("Exception RTPListener Changing Remote EndPoint. ->" +
                                                            changeExcp.Message);
                                    }
                                }
                            }
                        }
                        catch (Exception setSendExcp)
                        {
                            Logger.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.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.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.Logger.Error("Exception RTPSink ListenerClosed. ->" + excp.Message);
                    }
                }

                #endregion
            }
        }