背景
在RTCP的RR及SR中report block存在一个ntp字段,这个值为RR或SR产生时间戳。RFC3550的第4节描述了它的意义
Wallclock time (absolute date and time) is represented using the timestamp format of the Network Time Protocol (NTP), which is in seconds relative to 0h UTC on 1 January 1900 [4]. The full resolution NTP timestamp is a 64-bit unsigned fixed-point number with the integer part in the first 32 bits and the fractional part in the last 32 bits.
NTP的要点
首先明确:
- NTP时间戳有64位,其中32位表示second,32位表示 fraction second(后32位代表的时间单位的基数不同,并非是按正常基数60表示)。那么在秒后面的单位(毫秒,微秒等)则需要通过这个基数进行换算
the fraction portion is represented as a fraction of the total possible values. So it’s not a 1-to-1. For example, if I represent one second in 1000 parts, then someone gives me a value of .5 seconds with base 60, I’d have to scale that to my on base 1000. With NTP, the base is the 32 bit representation it’s transformed into. So 1 second = 2^32 ntp fraction seconds
- 表示的秒数起点为 0h UTC on 1 January 1900 (不同于Unix时间的起始于1970),fraction的单位为2^32 second
转换
明白了基本原理,就知道了怎么转换,首先需要定义两个常量,如下:
const uint32_t UnixNtpOffset = { 0x83AA7E80 };
const uint64_t NtpFractionalUnit = { 1LL << 32 };
UnixNtpOffset 表示0h UTC on 1 January 1900 到 0h UTC on 1 January 1970的秒数
NtpFractionalUnit 表示fraction second 单位
那么NTP可以定义如下:
struct Ntp
{
uint32_t seconds;
uint32_t fractions;
};
下面的代码描述如何转换(gettimeofday的struct timeval与NTP相互转换)
void ConvertUnixTimeIntoNtpTime(struct timeval *unix, Ntp *ntp)
{
ntp->seconds = unix->tv_sec + UnixNtpOffset;
ntp->fractions = (uint32_t)( (double)(unix->tv_usec) * (double)(NtpFractionalUnit) * 1.0e-6 );
}
void ConvertNtpTimeIntoUnixTime(Ntp*ntp, struct timeval *unix)
{
unix->tv_sec = ntp->seconds - UnixNtpOffset;
unix->tv_usec = (uint32_t)( (double)ntp->fractions * 1.0e6 / (double)(NtpFractionalUnit) );
}
struct timeval的tv_usev的单位为微妙,在转换为NTP的fraction时,先转换成秒。
封装
毫秒与NTP互转
class Time
{
// Seconds from Jan 1, 1900 to Jan 1, 1970.
static constexpr uint32_t UnixNtpOffset{ 0x83AA7E80 };
// NTP fractional unit.
static constexpr uint64_t NtpFractionalUnit{ 1LL << 32 };
public:
struct Ntp
{
uint32_t seconds;
uint32_t fractions;
};
static Time::Ntp TimeMs2Ntp(uint64_t ms);
static uint64_t Ntp2TimeMs(Time::Ntp ntp);
};
Time::Ntp Time::TimeMs2Ntp(uint64_t ms)
{
Time::Ntp ntp;
ntp.seconds = ms / 1000 + UnixNtpOffset;
ntp.fractions = static_cast<uint32_t>((static_cast<double>(ms % 1000) / 1000) * NtpFractionalUnit);
return ntp;
}
uint64_t Time::Ntp2TimeMs(Time::Ntp ntp)
{
return (static_cast<uint64_t>(ntp.seconds - UnixNtpOffset) * 1000 +
static_cast<uint64_t>(std::round((static_cast<double>(ntp.fractions) * 1000) / NtpFractionalUnit)));
}
参考:
https://stackoverflow.com/questions/2641954/create-ntp-time-stamp-from-gettimeofday