mirror of
				https://github.com/ossrs/srs.git
				synced 2025-03-09 15:49:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			267 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			267 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * SRT - Secure, Reliable, Transport
 | 
						|
 * Copyright (c) 2021 Haivision Systems Inc.
 | 
						|
 *
 | 
						|
 * This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
						|
 *
 | 
						|
 */
 | 
						|
#include "tsbpd_time.h"
 | 
						|
 | 
						|
#include "logging.h"
 | 
						|
#include "logger_defs.h"
 | 
						|
#include "packet.h"
 | 
						|
 | 
						|
using namespace srt_logging;
 | 
						|
using namespace srt::sync;
 | 
						|
 | 
						|
namespace srt
 | 
						|
{
 | 
						|
 | 
						|
#if SRT_DEBUG_TRACE_DRIFT
 | 
						|
class drift_logger
 | 
						|
{
 | 
						|
    typedef srt::sync::steady_clock steady_clock;
 | 
						|
 | 
						|
public:
 | 
						|
    drift_logger() {}
 | 
						|
 | 
						|
    ~drift_logger()
 | 
						|
    {
 | 
						|
        ScopedLock lck(m_mtx);
 | 
						|
        m_fout.close();
 | 
						|
    }
 | 
						|
 | 
						|
    void trace(unsigned                                   ackack_timestamp,
 | 
						|
               int                                        rtt_us,
 | 
						|
               int64_t                                    drift_sample,
 | 
						|
               int64_t                                    drift,
 | 
						|
               int64_t                                    overdrift,
 | 
						|
               const srt::sync::steady_clock::time_point& pkt_base,
 | 
						|
               const srt::sync::steady_clock::time_point& tsbpd_base)
 | 
						|
    {
 | 
						|
        using namespace srt::sync;
 | 
						|
        ScopedLock lck(m_mtx);
 | 
						|
        create_file();
 | 
						|
 | 
						|
        // std::string str_tnow = srt::sync::FormatTime(steady_clock::now());
 | 
						|
        // str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [STDY]' part
 | 
						|
 | 
						|
        std::string str_tbase = srt::sync::FormatTime(tsbpd_base);
 | 
						|
        str_tbase.resize(str_tbase.size() - 7); // remove trailing ' [STDY]' part
 | 
						|
 | 
						|
        std::string str_pkt_base = srt::sync::FormatTime(pkt_base);
 | 
						|
        str_pkt_base.resize(str_pkt_base.size() - 7); // remove trailing ' [STDY]' part
 | 
						|
 | 
						|
        // m_fout << str_tnow << ",";
 | 
						|
        m_fout << count_microseconds(steady_clock::now() - m_start_time) << ",";
 | 
						|
        m_fout << ackack_timestamp << ",";
 | 
						|
        m_fout << rtt_us << ",";
 | 
						|
        m_fout << drift_sample << ",";
 | 
						|
        m_fout << drift << ",";
 | 
						|
        m_fout << overdrift << ",";
 | 
						|
        m_fout << str_pkt_base << ",";
 | 
						|
        m_fout << str_tbase << "\n";
 | 
						|
        m_fout.flush();
 | 
						|
    }
 | 
						|
 | 
						|
private:
 | 
						|
    void print_header()
 | 
						|
    {
 | 
						|
        m_fout << "usElapsedStd,usAckAckTimestampStd,";
 | 
						|
        m_fout << "usRTTStd,usDriftSampleStd,usDriftStd,usOverdriftStd,tsPktBase,TSBPDBase\n";
 | 
						|
    }
 | 
						|
 | 
						|
    void create_file()
 | 
						|
    {
 | 
						|
        if (m_fout.is_open())
 | 
						|
            return;
 | 
						|
 | 
						|
        m_start_time         = srt::sync::steady_clock::now();
 | 
						|
        std::string str_tnow = srt::sync::FormatTimeSys(m_start_time);
 | 
						|
        str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part
 | 
						|
        while (str_tnow.find(':') != std::string::npos)
 | 
						|
        {
 | 
						|
            str_tnow.replace(str_tnow.find(':'), 1, 1, '_');
 | 
						|
        }
 | 
						|
        const std::string fname = "drift_trace_" + str_tnow + ".csv";
 | 
						|
        m_fout.open(fname, std::ofstream::out);
 | 
						|
        if (!m_fout)
 | 
						|
            std::cerr << "IPE: Failed to open " << fname << "!!!\n";
 | 
						|
 | 
						|
        print_header();
 | 
						|
    }
 | 
						|
 | 
						|
private:
 | 
						|
    srt::sync::Mutex                    m_mtx;
 | 
						|
    std::ofstream                       m_fout;
 | 
						|
    srt::sync::steady_clock::time_point m_start_time;
 | 
						|
};
 | 
						|
 | 
						|
drift_logger g_drift_logger;
 | 
						|
 | 
						|
#endif // SRT_DEBUG_TRACE_DRIFT
 | 
						|
 | 
						|
bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, const time_point& tsPktArrival, int usRTTSample)
 | 
						|
{
 | 
						|
    if (!m_bTsbPdMode)
 | 
						|
        return false;
 | 
						|
 | 
						|
    ScopedLock lck(m_mtxRW);
 | 
						|
 | 
						|
    // Remember the first RTT sample measured. Ideally we need RTT0 - the one from the handshaking phase,
 | 
						|
    // because TSBPD base is initialized there. But HS-based RTT is not yet implemented.
 | 
						|
    // Take the first one assuming it is close to RTT0.
 | 
						|
    if (m_iFirstRTT == -1)
 | 
						|
    {
 | 
						|
        m_iFirstRTT = usRTTSample;
 | 
						|
    }
 | 
						|
 | 
						|
    // A change in network delay has to be taken into account. The only way to get some estimation of it
 | 
						|
    // is to estimate RTT change and assume that the change of the one way network delay is
 | 
						|
    // approximated by the half of the RTT change.
 | 
						|
    const duration               tdRTTDelta    = usRTTSample >= 0 ? microseconds_from((usRTTSample - m_iFirstRTT) / 2) : duration(0);
 | 
						|
    const time_point             tsPktBaseTime = getPktTsbPdBaseTime(usPktTimestamp);
 | 
						|
    const steady_clock::duration tdDrift       = tsPktArrival - tsPktBaseTime - tdRTTDelta;
 | 
						|
 | 
						|
    const bool updated = m_DriftTracer.update(count_microseconds(tdDrift));
 | 
						|
 | 
						|
    if (updated)
 | 
						|
    {
 | 
						|
        IF_HEAVY_LOGGING(const steady_clock::time_point oldbase = m_tsTsbPdTimeBase);
 | 
						|
        steady_clock::duration overdrift = microseconds_from(m_DriftTracer.overdrift());
 | 
						|
        m_tsTsbPdTimeBase += overdrift;
 | 
						|
 | 
						|
        HLOGC(brlog.Debug,
 | 
						|
              log << "DRIFT=" << FormatDuration(tdDrift) << " AVG=" << (m_DriftTracer.drift() / 1000.0)
 | 
						|
                  << "ms, TB: " << FormatTime(oldbase) << " EXCESS: " << FormatDuration(overdrift)
 | 
						|
                  << " UPDATED TO: " << FormatTime(m_tsTsbPdTimeBase));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        HLOGC(brlog.Debug,
 | 
						|
              log << "DRIFT=" << FormatDuration(tdDrift) << " TB REMAINS: " << FormatTime(m_tsTsbPdTimeBase));
 | 
						|
    }
 | 
						|
 | 
						|
#if SRT_DEBUG_TRACE_DRIFT
 | 
						|
    g_drift_logger.trace(usPktTimestamp,
 | 
						|
                         usRTTSample,
 | 
						|
                         count_microseconds(tdDrift),
 | 
						|
                         m_DriftTracer.drift(),
 | 
						|
                         m_DriftTracer.overdrift(),
 | 
						|
                         tsPktBaseTime,
 | 
						|
                         m_tsTsbPdTimeBase);
 | 
						|
#endif
 | 
						|
    return updated;
 | 
						|
}
 | 
						|
 | 
						|
void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay)
 | 
						|
{
 | 
						|
    m_bTsbPdMode      = true;
 | 
						|
    m_bTsbPdWrapCheck = wrap;
 | 
						|
 | 
						|
    // Timebase passed here comes is calculated as:
 | 
						|
    // Tnow - hspkt.m_iTimeStamp
 | 
						|
    // where hspkt is the packet with SRT_CMD_HSREQ message.
 | 
						|
    //
 | 
						|
    // This function is called in the HSREQ reception handler only.
 | 
						|
    m_tsTsbPdTimeBase = timebase;
 | 
						|
    m_tdTsbPdDelay    = delay;
 | 
						|
}
 | 
						|
 | 
						|
void CTsbpdTime::applyGroupTime(const steady_clock::time_point& timebase,
 | 
						|
                                bool                            wrp,
 | 
						|
                                uint32_t                        delay,
 | 
						|
                                const steady_clock::duration&   udrift)
 | 
						|
{
 | 
						|
    // Same as setTsbPdMode, but predicted to be used for group members.
 | 
						|
    // This synchronizes the time from the INTERNAL TIMEBASE of an existing
 | 
						|
    // socket's internal timebase. This is required because the initial time
 | 
						|
    // base stays always the same, whereas the internal timebase undergoes
 | 
						|
    // adjustment as the 32-bit timestamps in the sockets wrap. The socket
 | 
						|
    // newly added to the group must get EXACTLY the same internal timebase
 | 
						|
    // or otherwise the TsbPd time calculation will ship different results
 | 
						|
    // on different member sockets.
 | 
						|
 | 
						|
    m_bTsbPdMode = true;
 | 
						|
 | 
						|
    m_tsTsbPdTimeBase = timebase;
 | 
						|
    m_bTsbPdWrapCheck = wrp;
 | 
						|
    m_tdTsbPdDelay    = microseconds_from(delay);
 | 
						|
    m_DriftTracer.forceDrift(count_microseconds(udrift));
 | 
						|
}
 | 
						|
 | 
						|
void CTsbpdTime::applyGroupDrift(const steady_clock::time_point& timebase,
 | 
						|
                                 bool                            wrp,
 | 
						|
                                 const steady_clock::duration&   udrift)
 | 
						|
{
 | 
						|
    // This is only when a drift was updated on one of the group members.
 | 
						|
    HLOGC(brlog.Debug,
 | 
						|
          log << "rcv-buffer: group synch uDRIFT: " << m_DriftTracer.drift() << " -> " << FormatDuration(udrift)
 | 
						|
              << " TB: " << FormatTime(m_tsTsbPdTimeBase) << " -> " << FormatTime(timebase));
 | 
						|
 | 
						|
    m_tsTsbPdTimeBase = timebase;
 | 
						|
    m_bTsbPdWrapCheck = wrp;
 | 
						|
 | 
						|
    m_DriftTracer.forceDrift(count_microseconds(udrift));
 | 
						|
}
 | 
						|
 | 
						|
CTsbpdTime::time_point CTsbpdTime::getTsbPdTimeBase(uint32_t timestamp_us) const
 | 
						|
{
 | 
						|
    // A data packet within [TSBPD_WRAP_PERIOD; 2 * TSBPD_WRAP_PERIOD] would end TSBPD wrap-aware state.
 | 
						|
    // Some incoming control packets may not update the TSBPD base (calling updateTsbPdTimeBase(..)),
 | 
						|
    // but may come before a data packet with a timestamp in this range. Therefore the whole range should be tracked.
 | 
						|
    const int64_t carryover_us =
 | 
						|
        (m_bTsbPdWrapCheck && timestamp_us <= 2 * TSBPD_WRAP_PERIOD) ? int64_t(CPacket::MAX_TIMESTAMP) + 1 : 0;
 | 
						|
 | 
						|
    return (m_tsTsbPdTimeBase + microseconds_from(carryover_us));
 | 
						|
}
 | 
						|
 | 
						|
CTsbpdTime::time_point CTsbpdTime::getPktTsbPdTime(uint32_t usPktTimestamp) const
 | 
						|
{
 | 
						|
    return getPktTsbPdBaseTime(usPktTimestamp) + m_tdTsbPdDelay + microseconds_from(m_DriftTracer.drift());
 | 
						|
}
 | 
						|
 | 
						|
CTsbpdTime::time_point CTsbpdTime::getPktTsbPdBaseTime(uint32_t usPktTimestamp) const
 | 
						|
{
 | 
						|
    return getTsbPdTimeBase(usPktTimestamp) + microseconds_from(usPktTimestamp);
 | 
						|
}
 | 
						|
 | 
						|
void CTsbpdTime::updateTsbPdTimeBase(uint32_t usPktTimestamp)
 | 
						|
{
 | 
						|
    if (m_bTsbPdWrapCheck)
 | 
						|
    {
 | 
						|
        // Wrap check period.
 | 
						|
        if ((usPktTimestamp >= TSBPD_WRAP_PERIOD) && (usPktTimestamp <= (TSBPD_WRAP_PERIOD * 2)))
 | 
						|
        {
 | 
						|
            /* Exiting wrap check period (if for packet delivery head) */
 | 
						|
            m_bTsbPdWrapCheck = false;
 | 
						|
            m_tsTsbPdTimeBase += microseconds_from(int64_t(CPacket::MAX_TIMESTAMP) + 1);
 | 
						|
            LOGC(tslog.Debug,
 | 
						|
                 log << "tsbpd wrap period ends with ts=" << usPktTimestamp << " - NEW TIME BASE: "
 | 
						|
                     << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us");
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Check if timestamp is within the TSBPD_WRAP_PERIOD before reaching the MAX_TIMESTAMP.
 | 
						|
    if (usPktTimestamp > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD))
 | 
						|
    {
 | 
						|
        // Approching wrap around point, start wrap check period (if for packet delivery head)
 | 
						|
        m_bTsbPdWrapCheck = true;
 | 
						|
        LOGC(tslog.Debug,
 | 
						|
             log << "tsbpd wrap period begins with ts=" << usPktTimestamp
 | 
						|
                 << " TIME BASE: " << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us.");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CTsbpdTime::getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const
 | 
						|
{
 | 
						|
    ScopedLock lck(m_mtxRW);
 | 
						|
    w_tb     = m_tsTsbPdTimeBase;
 | 
						|
    w_udrift = microseconds_from(m_DriftTracer.drift());
 | 
						|
    w_wrp    = m_bTsbPdWrapCheck;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace srt
 |