tsbpd_time.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. * SRT - Secure, Reliable, Transport
  3. * Copyright (c) 2021 Haivision Systems Inc.
  4. *
  5. * This Source Code Form is subject to the terms of the Mozilla Public
  6. * License, v. 2.0. If a copy of the MPL was not distributed with this
  7. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  8. *
  9. */
  10. #include "tsbpd_time.h"
  11. #include "logging.h"
  12. #include "logger_defs.h"
  13. #include "packet.h"
  14. using namespace srt_logging;
  15. using namespace srt::sync;
  16. namespace srt
  17. {
  18. #if SRT_DEBUG_TRACE_DRIFT
  19. class drift_logger
  20. {
  21. typedef srt::sync::steady_clock steady_clock;
  22. public:
  23. drift_logger() {}
  24. ~drift_logger()
  25. {
  26. ScopedLock lck(m_mtx);
  27. m_fout.close();
  28. }
  29. void trace(unsigned ackack_timestamp,
  30. int rtt_us,
  31. int64_t drift_sample,
  32. int64_t drift,
  33. int64_t overdrift,
  34. const srt::sync::steady_clock::time_point& pkt_base,
  35. const srt::sync::steady_clock::time_point& tsbpd_base)
  36. {
  37. using namespace srt::sync;
  38. ScopedLock lck(m_mtx);
  39. create_file();
  40. // std::string str_tnow = srt::sync::FormatTime(steady_clock::now());
  41. // str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [STDY]' part
  42. std::string str_tbase = srt::sync::FormatTime(tsbpd_base);
  43. str_tbase.resize(str_tbase.size() - 7); // remove trailing ' [STDY]' part
  44. std::string str_pkt_base = srt::sync::FormatTime(pkt_base);
  45. str_pkt_base.resize(str_pkt_base.size() - 7); // remove trailing ' [STDY]' part
  46. // m_fout << str_tnow << ",";
  47. m_fout << count_microseconds(steady_clock::now() - m_start_time) << ",";
  48. m_fout << ackack_timestamp << ",";
  49. m_fout << rtt_us << ",";
  50. m_fout << drift_sample << ",";
  51. m_fout << drift << ",";
  52. m_fout << overdrift << ",";
  53. m_fout << str_pkt_base << ",";
  54. m_fout << str_tbase << "\n";
  55. m_fout.flush();
  56. }
  57. private:
  58. void print_header()
  59. {
  60. m_fout << "usElapsedStd,usAckAckTimestampStd,";
  61. m_fout << "usRTTStd,usDriftSampleStd,usDriftStd,usOverdriftStd,tsPktBase,TSBPDBase\n";
  62. }
  63. void create_file()
  64. {
  65. if (m_fout.is_open())
  66. return;
  67. m_start_time = srt::sync::steady_clock::now();
  68. std::string str_tnow = srt::sync::FormatTimeSys(m_start_time);
  69. str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part
  70. while (str_tnow.find(':') != std::string::npos)
  71. {
  72. str_tnow.replace(str_tnow.find(':'), 1, 1, '_');
  73. }
  74. const std::string fname = "drift_trace_" + str_tnow + ".csv";
  75. m_fout.open(fname, std::ofstream::out);
  76. if (!m_fout)
  77. std::cerr << "IPE: Failed to open " << fname << "!!!\n";
  78. print_header();
  79. }
  80. private:
  81. srt::sync::Mutex m_mtx;
  82. std::ofstream m_fout;
  83. srt::sync::steady_clock::time_point m_start_time;
  84. };
  85. drift_logger g_drift_logger;
  86. #endif // SRT_DEBUG_TRACE_DRIFT
  87. bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, const time_point& tsPktArrival, int usRTTSample)
  88. {
  89. if (!m_bTsbPdMode)
  90. return false;
  91. ScopedLock lck(m_mtxRW);
  92. // Remember the first RTT sample measured. Ideally we need RTT0 - the one from the handshaking phase,
  93. // because TSBPD base is initialized there. But HS-based RTT is not yet implemented.
  94. // Take the first one assuming it is close to RTT0.
  95. if (m_iFirstRTT == -1)
  96. {
  97. m_iFirstRTT = usRTTSample;
  98. }
  99. // A change in network delay has to be taken into account. The only way to get some estimation of it
  100. // is to estimate RTT change and assume that the change of the one way network delay is
  101. // approximated by the half of the RTT change.
  102. const duration tdRTTDelta = usRTTSample >= 0 ? microseconds_from((usRTTSample - m_iFirstRTT) / 2) : duration(0);
  103. const time_point tsPktBaseTime = getPktTsbPdBaseTime(usPktTimestamp);
  104. const steady_clock::duration tdDrift = tsPktArrival - tsPktBaseTime - tdRTTDelta;
  105. const bool updated = m_DriftTracer.update(count_microseconds(tdDrift));
  106. if (updated)
  107. {
  108. IF_HEAVY_LOGGING(const steady_clock::time_point oldbase = m_tsTsbPdTimeBase);
  109. steady_clock::duration overdrift = microseconds_from(m_DriftTracer.overdrift());
  110. m_tsTsbPdTimeBase += overdrift;
  111. HLOGC(brlog.Debug,
  112. log << "DRIFT=" << FormatDuration(tdDrift) << " AVG=" << (m_DriftTracer.drift() / 1000.0)
  113. << "ms, TB: " << FormatTime(oldbase) << " EXCESS: " << FormatDuration(overdrift)
  114. << " UPDATED TO: " << FormatTime(m_tsTsbPdTimeBase));
  115. }
  116. else
  117. {
  118. HLOGC(brlog.Debug,
  119. log << "DRIFT=" << FormatDuration(tdDrift) << " TB REMAINS: " << FormatTime(m_tsTsbPdTimeBase));
  120. }
  121. #if SRT_DEBUG_TRACE_DRIFT
  122. g_drift_logger.trace(usPktTimestamp,
  123. usRTTSample,
  124. count_microseconds(tdDrift),
  125. m_DriftTracer.drift(),
  126. m_DriftTracer.overdrift(),
  127. tsPktBaseTime,
  128. m_tsTsbPdTimeBase);
  129. #endif
  130. return updated;
  131. }
  132. void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay)
  133. {
  134. m_bTsbPdMode = true;
  135. m_bTsbPdWrapCheck = wrap;
  136. // Timebase passed here comes is calculated as:
  137. // Tnow - hspkt.m_iTimeStamp
  138. // where hspkt is the packet with SRT_CMD_HSREQ message.
  139. //
  140. // This function is called in the HSREQ reception handler only.
  141. m_tsTsbPdTimeBase = timebase;
  142. m_tdTsbPdDelay = delay;
  143. }
  144. void CTsbpdTime::applyGroupTime(const steady_clock::time_point& timebase,
  145. bool wrp,
  146. uint32_t delay,
  147. const steady_clock::duration& udrift)
  148. {
  149. // Same as setTsbPdMode, but predicted to be used for group members.
  150. // This synchronizes the time from the INTERNAL TIMEBASE of an existing
  151. // socket's internal timebase. This is required because the initial time
  152. // base stays always the same, whereas the internal timebase undergoes
  153. // adjustment as the 32-bit timestamps in the sockets wrap. The socket
  154. // newly added to the group must get EXACTLY the same internal timebase
  155. // or otherwise the TsbPd time calculation will ship different results
  156. // on different member sockets.
  157. m_bTsbPdMode = true;
  158. m_tsTsbPdTimeBase = timebase;
  159. m_bTsbPdWrapCheck = wrp;
  160. m_tdTsbPdDelay = microseconds_from(delay);
  161. m_DriftTracer.forceDrift(count_microseconds(udrift));
  162. }
  163. void CTsbpdTime::applyGroupDrift(const steady_clock::time_point& timebase,
  164. bool wrp,
  165. const steady_clock::duration& udrift)
  166. {
  167. // This is only when a drift was updated on one of the group members.
  168. HLOGC(brlog.Debug,
  169. log << "rcv-buffer: group synch uDRIFT: " << m_DriftTracer.drift() << " -> " << FormatDuration(udrift)
  170. << " TB: " << FormatTime(m_tsTsbPdTimeBase) << " -> " << FormatTime(timebase));
  171. m_tsTsbPdTimeBase = timebase;
  172. m_bTsbPdWrapCheck = wrp;
  173. m_DriftTracer.forceDrift(count_microseconds(udrift));
  174. }
  175. CTsbpdTime::time_point CTsbpdTime::getTsbPdTimeBase(uint32_t timestamp_us) const
  176. {
  177. // A data packet within [TSBPD_WRAP_PERIOD; 2 * TSBPD_WRAP_PERIOD] would end TSBPD wrap-aware state.
  178. // Some incoming control packets may not update the TSBPD base (calling updateTsbPdTimeBase(..)),
  179. // but may come before a data packet with a timestamp in this range. Therefore the whole range should be tracked.
  180. const int64_t carryover_us =
  181. (m_bTsbPdWrapCheck && timestamp_us <= 2 * TSBPD_WRAP_PERIOD) ? int64_t(CPacket::MAX_TIMESTAMP) + 1 : 0;
  182. return (m_tsTsbPdTimeBase + microseconds_from(carryover_us));
  183. }
  184. CTsbpdTime::time_point CTsbpdTime::getPktTsbPdTime(uint32_t usPktTimestamp) const
  185. {
  186. return getPktTsbPdBaseTime(usPktTimestamp) + m_tdTsbPdDelay + microseconds_from(m_DriftTracer.drift());
  187. }
  188. CTsbpdTime::time_point CTsbpdTime::getPktTsbPdBaseTime(uint32_t usPktTimestamp) const
  189. {
  190. return getTsbPdTimeBase(usPktTimestamp) + microseconds_from(usPktTimestamp);
  191. }
  192. void CTsbpdTime::updateTsbPdTimeBase(uint32_t usPktTimestamp)
  193. {
  194. if (m_bTsbPdWrapCheck)
  195. {
  196. // Wrap check period.
  197. if ((usPktTimestamp >= TSBPD_WRAP_PERIOD) && (usPktTimestamp <= (TSBPD_WRAP_PERIOD * 2)))
  198. {
  199. /* Exiting wrap check period (if for packet delivery head) */
  200. m_bTsbPdWrapCheck = false;
  201. m_tsTsbPdTimeBase += microseconds_from(int64_t(CPacket::MAX_TIMESTAMP) + 1);
  202. LOGC(tslog.Debug,
  203. log << "tsbpd wrap period ends with ts=" << usPktTimestamp << " - NEW TIME BASE: "
  204. << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us");
  205. }
  206. return;
  207. }
  208. // Check if timestamp is within the TSBPD_WRAP_PERIOD before reaching the MAX_TIMESTAMP.
  209. if (usPktTimestamp > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD))
  210. {
  211. // Approching wrap around point, start wrap check period (if for packet delivery head)
  212. m_bTsbPdWrapCheck = true;
  213. LOGC(tslog.Debug,
  214. log << "tsbpd wrap period begins with ts=" << usPktTimestamp
  215. << " TIME BASE: " << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us.");
  216. }
  217. }
  218. void CTsbpdTime::getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const
  219. {
  220. ScopedLock lck(m_mtxRW);
  221. w_tb = m_tsTsbPdTimeBase;
  222. w_udrift = microseconds_from(m_DriftTracer.drift());
  223. w_wrp = m_bTsbPdWrapCheck;
  224. }
  225. } // namespace srt