Пример #1
0
        private void Send()
        {
            try
            {
                int       payloadSize = RTPPacketSendSize;
                RTPPacket rtpPacket   = new RTPPacket(RTPPacketSendSize);
                byte[]    rtpBytes    = rtpPacket.GetBytes();

                RTPHeader rtpHeader = new RTPHeader();
                rtpHeader.SequenceNumber = (UInt16)65000;  //Convert.ToUInt16(Crypto.GetRandomInt(0, UInt16.MaxValue));
                uint   sendTimestamp     = uint.MaxValue - 5000;
                uint   lastSendTimestamp = sendTimestamp;
                UInt16 lastSeqNum        = 0;

                logger.Debug("RTP send stream starting to " + IPSocket.GetSocketString(m_streamEndPoint) +
                             " with payload size " + payloadSize + " bytes.");

                Sending            = true;
                m_startRTPSendTime = DateTime.MinValue;
                m_lastRTPSentTime  = DateTime.MinValue;
                m_sampleStartSeqNo = rtpHeader.SequenceNumber;

                DateTime lastRTPSendAttempt = DateTime.Now;

                while (m_udpListener != null && !StopListening)
                {
                    // This may be changed by the listener so it needs to be set each iteration.
                    IPEndPoint dstEndPoint = m_streamEndPoint;

                    //logger.Info("Sending RTP packet to " + dstEndPoint.Address + ":"  + dstEndPoint.Port);

                    if (payloadSize != m_rtpPacketSendSize)
                    {
                        payloadSize = m_rtpPacketSendSize;
                        logger.Info("Changing RTP payload to " + payloadSize);
                        rtpPacket = new RTPPacket(RTP_HEADER_SIZE + m_rtpPacketSendSize);
                        rtpBytes  = rtpPacket.GetBytes();
                    }

                    try
                    {
                        if (m_startRTPSendTime == DateTime.MinValue)
                        {
                            m_startRTPSendTime  = DateTime.Now;
                            rtpHeader.MarkerBit = 0;

                            logger.Debug("RTPSink Send SyncSource=" + rtpPacket.Header.SyncSource + ".");
                        }
                        else
                        {
                            lastSendTimestamp = sendTimestamp;
                            double milliSinceLastSend = DateTime.Now.Subtract(m_lastRTPSentTime).TotalMilliseconds;
                            sendTimestamp =
                                Convert.ToUInt32((lastSendTimestamp + (milliSinceLastSend * TIMESTAMP_FACTOR)) %
                                                 uint.MaxValue);

                            if (lastSendTimestamp > sendTimestamp)
                            {
                                logger.Error("RTP Sender previous timestamp (" + lastSendTimestamp + ") > timestamp (" +
                                             sendTimestamp + ") ms since last send=" + milliSinceLastSend +
                                             ", lastseqnum=" + lastSeqNum + ", seqnum=" + rtpHeader.SequenceNumber +
                                             ".");
                            }

                            if (DateTime.Now.Subtract(m_lastRTPSentTime).TotalMilliseconds > 75)
                            {
                                logger.Debug("delayed send: " + rtpHeader.SequenceNumber + ", time since last send " +
                                             DateTime.Now.Subtract(m_lastRTPSentTime).TotalMilliseconds + "ms.");
                            }
                        }

                        rtpHeader.Timestamp = sendTimestamp;
                        byte[] rtpHeaderBytes = rtpHeader.GetBytes();
                        Array.Copy(rtpHeaderBytes, 0, rtpBytes, 0, rtpHeaderBytes.Length);

                        // Send RTP packets and any extra channels required to emulate mutliple calls.
                        for (int channelCount = 0; channelCount < m_channels; channelCount++)
                        {
                            //logger.Debug("Send rtp getting wallclock timestamp for " + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss:fff"));
                            //DateTime sendTime = DateTime.Now;
                            //rtpHeader.Timestamp = RTPHeader.GetWallclockUTCStamp(sendTime);
                            //logger.Debug(rtpHeader.SequenceNumber + "," + rtpHeader.Timestamp);

                            m_udpListener.Send(rtpBytes, rtpBytes.Length, dstEndPoint);
                            m_lastRTPSentTime = DateTime.Now;

                            m_packetsSent++;
                            m_bytesSent += rtpBytes.Length;

                            if (m_packetsSent % 500 == 0)
                            {
                                logger.Debug("Total packets sent to " + dstEndPoint.ToString() + " " + m_packetsSent +
                                             ", bytes " + NumberFormatter.ToSIByteFormat(m_bytesSent, 2) + ".");
                            }

                            //sendLogger.Info(m_lastRTPSentTime.ToString("dd MMM yyyy HH:mm:ss:fff") + "," + m_lastRTPSentTime.Subtract(m_startRTPSendTime).TotalMilliseconds.ToString("0") + "," + rtpHeader.SequenceNumber + "," + rtpBytes.Length);

                            //sendLogger.Info(rtpHeader.SequenceNumber + "," + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss:fff"));

                            if (DataSent != null)
                            {
                                try
                                {
                                    DataSent(m_streamId, rtpBytes, dstEndPoint);
                                }
                                catch (Exception excp)
                                {
                                    logger.Error("Exception RTPSink DataSent. " + excp.Message);
                                }
                            }

                            lastSeqNum = rtpHeader.SequenceNumber;
                            if (rtpHeader.SequenceNumber == UInt16.MaxValue)
                            {
                                //logger.Debug("RTPSink looping  the sequence number in sample.");
                                rtpHeader.SequenceNumber = 0;
                            }
                            else
                            {
                                rtpHeader.SequenceNumber++;
                            }
                        }
                    }
                    catch (Exception excp)
                    {
                        logger.Error("Exception RTP Send. " + excp.GetType() + ". " + excp.Message);

                        if (excp.GetType() == typeof(SocketException))
                        {
                            logger.Error("socket exception errorcode=" + ((SocketException)excp).ErrorCode + ".");
                        }

                        logger.Warn("Remote socket closed on send. Last RTP recevied " +
                                    m_lastRTPReceivedTime.ToString("dd MMM yyyy HH:mm:ss") +
                                    ", last RTP successfull send " +
                                    m_lastRTPSentTime.ToString("dd MMM yyyy HH:mm:ss") + ".");
                    }

                    Thread.Sleep(RTPFrameSize);

                    #region Check for whether the stream has timed out on a send or receive and if so shut down the stream.

                    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;
                    double testDuration = DateTime.Now.Subtract(m_startRTPSendTime).TotalSeconds;

                    if ((
                            noRTPRcvdDuration > NO_RTP_TIMEOUT ||
                            noRTPSentDuration > NO_RTP_TIMEOUT ||
                            (m_lastRTPReceivedTime == DateTime.MinValue && testDuration > NO_RTP_TIMEOUT)
                            ) && // If the test request comes from a private or unreachable IP address then no RTP will ever be received.
                        StopIfNoData)
                    {
                        logger.Warn("Disconnecting RTP stream on " + m_localEndPoint.Address.ToString() + ":" +
                                    m_localEndPoint.Port + " due to not being able to send any RTP for " +
                                    NO_RTP_TIMEOUT + "s.");
                        StopListening = true;
                    }

                    // Shutdown the socket even if there is still RTP but the stay alive limit has been exceeded.
                    if (RTPMaxStayAlive > 0 && DateTime.Now.Subtract(m_startRTPSendTime).TotalSeconds > RTPMaxStayAlive)
                    {
                        logger.Warn("Shutting down RTPSink due to passing RTPMaxStayAlive time.");
                        Shutdown();
                        StopListening = true;
                    }

                    #endregion
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception Send RTPSink: " + excp.Message);
            }
            finally
            {
                #region Shut down socket.

                Shutdown();

                if (SenderClosed != null)
                {
                    try
                    {
                        SenderClosed(m_streamId, m_callDescriptorId);
                    }
                    catch (Exception excp)
                    {
                        logger.Error("Exception RTPSink SenderClosed. " + excp.Message);
                    }
                }

                #endregion
            }
        }
Пример #2
0
        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;
                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.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
            }
        }
Пример #3
0
        /// <summary>
        /// Sends a dynamically sized frame. The RTP marker bit will be set for the last transmitted packet in the frame.
        /// </summary>
        /// <param name="frame">The frame to transmit.</param>
        /// <param name="frameSpacing">The increment to add to the RTP timestamp for each new frame.</param>
        /// <param name="payloadType">The payload type to set on the RTP packet.</param>
        public void SendVP8Frame(byte[] frame, uint frameSpacing, int payloadType)
        {
            try
            {
                if (!IsRunning)
                {
                    logger.Warn("SendVP8Frame cannot be called on a closed RTP channel.");
                }
                else if (_rtpSocketError != SocketError.Success)
                {
                    logger.Warn("SendVP8Frame was called for an RTP socket in an error state of " + _rtpSocketError +
                                ".");
                }
                else
                {
                    _timestamp = (_timestamp == 0)
                        ? DateTimeToNptTimestamp32(DateTime.Now)
                        : (_timestamp + frameSpacing) % UInt32.MaxValue;

                    //System.Diagnostics.Debug.WriteLine("Sending " + frame.Length + " encoded bytes to client, timestamp " + _timestamp + ", starting sequence number " + _sequenceNumber + ".");

                    for (int index = 0; index *RTP_MAX_PAYLOAD < frame.Length; index++)
                    {
                        //byte[] vp8HeaderBytes = (index == 0) ? new byte[VP8_RTP_HEADER_LENGTH] { 0x90, 0x80, (byte)(_sequenceNumber % 128) } : new byte[VP8_RTP_HEADER_LENGTH] { 0x80, 0x80, (byte)(_sequenceNumber % 128) };
                        byte[] vp8HeaderBytes = (index == 0)
                            ? new byte[VP8_RTP_HEADER_LENGTH] {
                            0x10
                        }
                            : new byte[VP8_RTP_HEADER_LENGTH] {
                            0x00
                        };

                        int offset        = index * RTP_MAX_PAYLOAD;
                        int payloadLength = ((index + 1) * RTP_MAX_PAYLOAD < frame.Length)
                            ? RTP_MAX_PAYLOAD
                            : frame.Length - index * RTP_MAX_PAYLOAD;

                        // RTPPacket rtpPacket = new RTPPacket(payloadLength + VP8_RTP_HEADER_LENGTH + ((_srtp != null) ? SRTP_SIGNATURE_LENGTH : 0));
                        RTPPacket rtpPacket = new RTPPacket(payloadLength + VP8_RTP_HEADER_LENGTH);
                        rtpPacket.Header.SyncSource     = _syncSource;
                        rtpPacket.Header.SequenceNumber = _sequenceNumber++;
                        rtpPacket.Header.Timestamp      = _timestamp;
                        rtpPacket.Header.MarkerBit      = (offset + payloadLength >= frame.Length) ? 1 : 0;
                        rtpPacket.Header.PayloadType    = payloadType;

                        Buffer.BlockCopy(vp8HeaderBytes, 0, rtpPacket.Payload, 0, vp8HeaderBytes.Length);
                        Buffer.BlockCopy(frame, offset, rtpPacket.Payload, vp8HeaderBytes.Length, payloadLength);

                        byte[] rtpBytes = rtpPacket.GetBytes();

                        //if (_srtp != null)
                        //{
                        //    int rtperr = _srtp.ProtectRTP(rtpBytes, rtpBytes.Length - SRTP_SIGNATURE_LENGTH);
                        //    if (rtperr != 0)
                        //    {
                        //        logger.Warn("An error was returned attempting to sign an SRTP packet for " + _remoteEndPoint + ", error code " + rtperr + ".");
                        //    }
                        //}

                        //System.Diagnostics.Debug.WriteLine(" offset " + (index * RTP_MAX_PAYLOAD) + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header .MarkerBit + ".");

                        //Stopwatch sw = new Stopwatch();
                        //sw.Start();

                        _rtpSocket.SendTo(rtpBytes, rtpBytes.Length, SocketFlags.None, _remoteEP);

                        //sw.Stop();

                        //if (sw.ElapsedMilliseconds > 15)
                        //{
                        //    logger.Warn(" SendVP8Frame offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + ", took " + sw.ElapsedMilliseconds + "ms.");
                        //}
                    }
                }
            }
            catch (Exception excp)
            {
                if (IsRunning)
                {
                    logger.Warn("Exception RTPChannel.SendVP8Frame attempting to send to the RTP socket at " +
                                _remoteEP + ". " + excp);

                    if (OnRTPSocketDisconnected != null)
                    {
                        OnRTPSocketDisconnected();
                    }
                }
            }
        }
Пример #4
0
        /// <summary>
        /// H264 frames need a two byte header when transmitted over RTP.
        /// </summary>
        /// <param name="frame">The H264 encoded frame to transmit.</param>
        /// <param name="frameSpacing">The increment to add to the RTP timestamp for each new frame.</param>
        /// <param name="payloadType">The payload type to set on the RTP packet.</param>
        public void SendH264Frame(byte[] frame, uint frameSpacing, int payloadType)
        {
            try
            {
                if (!IsRunning)
                {
                    logger.Warn("SendH264Frame cannot be called on a closed session.");
                }
                else if (_rtpSocketError != SocketError.Success)
                {
                    logger.Warn("SendH264Frame was called for an RTP socket in an error state of " + _rtpSocketError +
                                ".");
                }
                else
                {
                    _timestamp = (_timestamp == 0)
                        ? DateTimeToNptTimestamp32(DateTime.Now)
                        : (_timestamp + frameSpacing) % UInt32.MaxValue;

                    //System.Diagnostics.Debug.WriteLine("Sending " + frame.Length + " H264 encoded bytes to client, timestamp " + _timestamp + ", starting sequence number " + _sequenceNumber + ".");

                    for (int index = 0; index *RTP_MAX_PAYLOAD < frame.Length; index++)
                    {
                        uint offset        = Convert.ToUInt32(index * RTP_MAX_PAYLOAD);
                        int  payloadLength = ((index + 1) * RTP_MAX_PAYLOAD < frame.Length)
                            ? RTP_MAX_PAYLOAD
                            : frame.Length - index * RTP_MAX_PAYLOAD;

                        RTPPacket rtpPacket = new RTPPacket(payloadLength + H264_RTP_HEADER_LENGTH);
                        rtpPacket.Header.SyncSource     = _syncSource;
                        rtpPacket.Header.SequenceNumber = _sequenceNumber++;
                        rtpPacket.Header.Timestamp      = _timestamp;
                        rtpPacket.Header.MarkerBit      = 0;
                        rtpPacket.Header.PayloadType    = payloadType;

                        // Start RTP packet in frame 0x1c 0x89
                        // Middle RTP packet in frame 0x1c 0x09
                        // Last RTP packet in frame 0x1c 0x49

                        byte[] h264Header = new byte[] { 0x1c, 0x09 };

                        if (index == 0 && frame.Length < RTP_MAX_PAYLOAD)
                        {
                            // First and last RTP packet in the frame.
                            h264Header = new byte[] { 0x1c, 0x49 };
                            rtpPacket.Header.MarkerBit = 1;
                        }
                        else if (index == 0)
                        {
                            h264Header = new byte[] { 0x1c, 0x89 };
                        }
                        else if ((index + 1) * RTP_MAX_PAYLOAD > frame.Length)
                        {
                            h264Header = new byte[] { 0x1c, 0x49 };
                            rtpPacket.Header.MarkerBit = 1;
                        }

                        var h264Stream = frame.Skip(index * RTP_MAX_PAYLOAD).Take(payloadLength).ToList();
                        h264Stream.InsertRange(0, h264Header);
                        rtpPacket.Payload = h264Stream.ToArray();

                        byte[] rtpBytes = rtpPacket.GetBytes();

                        //System.Diagnostics.Debug.WriteLine(" offset " + (index * RTP_MAX_PAYLOAD) + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header .MarkerBit + ".");

                        //Stopwatch sw = new Stopwatch();
                        //sw.Start();

                        _rtpSocket.SendTo(rtpBytes, rtpBytes.Length, SocketFlags.None, _remoteEP);

                        //sw.Stop();

                        //if (sw.ElapsedMilliseconds > 15)
                        //{
                        //    logger.Warn(" SendH264Frame offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + ", took " + sw.ElapsedMilliseconds + "ms.");
                        //}
                    }
                }
            }
            catch (Exception excp)
            {
                if (IsRunning)
                {
                    logger.Warn("Exception RTPChannel.SendH264Frame attempting to send to the RTP socket at " +
                                _remoteEP + ". " + excp);

                    if (OnRTPSocketDisconnected != null)
                    {
                        OnRTPSocketDisconnected();
                    }
                }
            }
        }
Пример #5
0
        /// <summary>
        /// Helper method to send a low quality JPEG image over RTP. This method supports a very abbreviated version of RFC 2435 "RTP Payload Format for JPEG-compressed Video".
        /// It's intended as a quick convenient way to send something like a test pattern image over an RTSP connection. More than likely it won't be suitable when a high
        /// quality image is required since the header used in this method does not support quantization tables.
        /// </summary>
        /// <param name="jpegBytes">The raw encoded bytes of teh JPEG image to transmit.</param>
        /// <param name="jpegQuality">The encoder quality of the JPEG image.</param>
        /// <param name="jpegWidth">The width of the JPEG image.</param>
        /// <param name="jpegHeight">The height of the JPEG image.</param>
        /// <param name="framesPerSecond">The rate at which the JPEG frames are being transmitted at. used to calculate the timestamp.</param>
        public void SendJpegFrame(byte[] jpegBytes, int jpegQuality, int jpegWidth, int jpegHeight, int framesPerSecond)
        {
            try
            {
                if (!IsRunning)
                {
                    logger.Warn("SendJpegFrame cannot be called on a closed session.");
                }
                else if (_rtpSocketError != SocketError.Success)
                {
                    logger.Warn("SendJpegFrame was called for an RTP socket in an error state of " + _rtpSocketError +
                                ".");
                }
                else
                {
                    _timestamp = (_timestamp == 0)
                        ? DateTimeToNptTimestamp32(DateTime.Now)
                        : (_timestamp + (uint)(RFC_2435_FREQUENCY_BASELINE / framesPerSecond)) % UInt32.MaxValue;

                    //System.Diagnostics.Debug.WriteLine("Sending " + jpegBytes.Length + " encoded bytes to client, timestamp " + _timestamp + ", starting sequence number " + _sequenceNumber + ", image dimensions " + jpegWidth + " x " + jpegHeight + ".");

                    for (int index = 0; index *RTP_MAX_PAYLOAD < jpegBytes.Length; index++)
                    {
                        uint offset        = Convert.ToUInt32(index * RTP_MAX_PAYLOAD);
                        int  payloadLength = ((index + 1) * RTP_MAX_PAYLOAD < jpegBytes.Length)
                            ? RTP_MAX_PAYLOAD
                            : jpegBytes.Length - index * RTP_MAX_PAYLOAD;

                        byte[] jpegHeader = CreateLowQualityRtpJpegHeader(offset, jpegQuality, jpegWidth, jpegHeight);

                        List <byte> packetPayload = new List <byte>();
                        packetPayload.AddRange(jpegHeader);
                        packetPayload.AddRange(jpegBytes.Skip(index * RTP_MAX_PAYLOAD).Take(payloadLength));

                        RTPPacket rtpPacket = new RTPPacket(packetPayload.Count);
                        rtpPacket.Header.SyncSource     = _syncSource;
                        rtpPacket.Header.SequenceNumber = _sequenceNumber++;
                        rtpPacket.Header.Timestamp      = _timestamp;
                        rtpPacket.Header.MarkerBit      = ((index + 1) * RTP_MAX_PAYLOAD < jpegBytes.Length) ? 0 : 1;
                        rtpPacket.Header.PayloadType    = (int)SDPMediaFormatsEnum.JPEG;
                        rtpPacket.Payload = packetPayload.ToArray();

                        byte[] rtpBytes = rtpPacket.GetBytes();

                        //System.Diagnostics.Debug.WriteLine(" offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + ".");

                        //Stopwatch sw = new Stopwatch();
                        //sw.Start();

                        _rtpSocket.SendTo(rtpBytes, _remoteEP);

                        //sw.Stop();

                        //if (sw.ElapsedMilliseconds > 15)
                        //{
                        //    logger.Warn(" SendJpegFrame offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + ", took " + sw.ElapsedMilliseconds + "ms.");
                        //}
                    }

                    //sw.Stop();
                    //System.Diagnostics.Debug.WriteLine("SendJpegFrame took " + sw.ElapsedMilliseconds + ".");
                }
            }
            catch (Exception excp)
            {
                if (IsRunning)
                {
                    logger.Warn("Exception RTPChannel.SendJpegFrame attempting to send to the RTP socket at " +
                                _remoteEP + ". " + excp);
                    //_rtpSocketError = SocketError.SocketError;

                    if (OnRTPSocketDisconnected != null)
                    {
                        OnRTPSocketDisconnected();
                    }
                }
            }
        }
Пример #6
0
        private void ProcessRTPPackets()
        {
            try
            {
                Thread.CurrentThread.Name = "rtspclient-rtp";

                _lastRTPReceivedAt = DateTime.Now;
                _lastBWCalcAt = DateTime.Now;

                while (!_isClosed)
                {
                    while (_rtspSession.HasRTPPacket())
                    {
                        RTPPacket rtpPacket = _rtspSession.GetNextRTPPacket();

                        if (rtpPacket != null)
                        {
                            _lastRTPReceivedAt = DateTime.Now;
                            _bytesSinceLastBWCalc += RTPHeader.MIN_HEADER_LEN + rtpPacket.Payload.Length;

                            if (_rtpTrackingAction != null)
                            {
                                double bwCalcSeconds = DateTime.Now.Subtract(_lastBWCalcAt).TotalSeconds;
                                if (bwCalcSeconds > BANDWIDTH_CALCULATION_SECONDS)
                                {
                                    _lastBWCalc = _bytesSinceLastBWCalc * 8 / bwCalcSeconds;
                                    _lastFrameRate = _framesSinceLastCalc / bwCalcSeconds;
                                    _bytesSinceLastBWCalc = 0;
                                    _framesSinceLastCalc = 0;
                                    _lastBWCalcAt = DateTime.Now;
                                }

                                var abbrevURL = (_url.Length <= 50) ? _url : _url.Substring(0, 50);
                                string rtpTrackingText = String.Format("Url: {0}\r\nRcvd At: {1}\r\nSeq Num: {2}\r\nTS: {3}\r\nPayoad: {4}\r\nFrame Size: {5}\r\nBW: {6}\r\nFrame Rate: {7}", abbrevURL, DateTime.Now.ToString("HH:mm:ss:fff"), rtpPacket.Header.SequenceNumber, rtpPacket.Header.Timestamp, ((SDPMediaFormatsEnum)rtpPacket.Header.PayloadType).ToString(), _lastFrameSize + " bytes", _lastBWCalc.ToString("0.#") + "bps", _lastFrameRate.ToString("0.##") + "fps");
                                _rtpTrackingAction(rtpTrackingText);
                            }

                            if (rtpPacket.Header.Timestamp < _lastCompleteFrameTimestamp)
                            {
                                System.Diagnostics.Debug.WriteLine("Ignoring RTP packet with timestamp " + rtpPacket.Header.Timestamp + " as it's earlier than the last complete frame.");
                            }
                            else
                            {
                                while (_frames.Count > MAX_FRAMES_QUEUE_LENGTH)
                                {
                                    var oldestFrame = _frames.OrderBy(x => x.Timestamp).First();
                                    _frames.Remove(oldestFrame);
                                    System.Diagnostics.Debug.WriteLine("Receive queue full, dropping oldest frame with timestamp " + oldestFrame.Timestamp + ".");
                                }

                                var frame = _frames.Where(x => x.Timestamp == rtpPacket.Header.Timestamp).SingleOrDefault();

                                if (frame == null)
                                {
                                    frame = new RTPFrame() { Timestamp = rtpPacket.Header.Timestamp, HasMarker = rtpPacket.Header.MarkerBit == 1 };
                                    frame.AddRTPPacket(rtpPacket);
                                    _frames.Add(frame);
                                }
                                else
                                {
                                    frame.HasMarker = (rtpPacket.Header.MarkerBit == 1);
                                    frame.AddRTPPacket(rtpPacket);
                                }

                                if (frame.IsComplete)
                                {
                                    // The frame is ready for handing over to the UI.
                                    byte[] imageBytes = frame.GetFramePayload();

                                    _lastFrameSize = imageBytes.Length;
                                    _framesSinceLastCalc++;

                                    _lastCompleteFrameTimestamp = rtpPacket.Header.Timestamp;
                                    //System.Diagnostics.Debug.WriteLine("Frame ready " + frame.Timestamp + ", sequence numbers " + frame.StartSequenceNumber + " to " + frame.EndSequenceNumber + ",  payload length " + imageBytes.Length + ".");
                                    //logger.Debug("Frame ready " + frame.Timestamp + ", sequence numbers " + frame.StartSequenceNumber + " to " + frame.EndSequenceNumber + ",  payload length " + imageBytes.Length + ".");
                                    _frames.Remove(frame);

                                    // Also remove any earlier frames as we don't care about anything that's earlier than the current complete frame.
                                    foreach (var oldFrame in _frames.Where(x => x.Timestamp <= rtpPacket.Header.Timestamp).ToList())
                                    {
                                        System.Diagnostics.Debug.WriteLine("Discarding old frame for timestamp " + oldFrame.Timestamp + ".");
                                        logger.Warn("Discarding old frame for timestamp " + oldFrame.Timestamp + ".");
                                        _frames.Remove(oldFrame);
                                    }

                                    if (OnFrameReady != null)
                                    {
                                        try
                                        {
                                            //if (frame.FramePackets.Count == 1)
                                            //{
                                            //    // REMOVE.
                                            //    logger.Warn("Discarding frame as there should have been more than 1 RTP packets.");
                                            //}
                                            //else
                                            //{
                                                //System.Diagnostics.Debug.WriteLine("RTP frame ready for timestamp " + frame.Timestamp + ".");
                                                OnFrameReady(this, frame);
                                            //}
                                        }
                                        catch (Exception frameReadyExcp)
                                        {
                                            logger.Error("Exception RTSPClient.ProcessRTPPackets OnFrameReady. " + frameReadyExcp);
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (DateTime.Now.Subtract(_lastRTPReceivedAt).TotalSeconds > RTP_TIMEOUT_SECONDS)
                    {
                        logger.Warn("No RTP packets were received on RTSP session " + _rtspSession.SessionID + " for " + RTP_TIMEOUT_SECONDS + ". The session will now be closed.");
                        Close();
                    }
                    else
                    {
                        Thread.Sleep(1);
                    }
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception RTSPClient.ProcessRTPPackets. " + excp);
            }
        }