2018-02-26 19:35:16 -05:00
|
|
|
/*
|
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
This file is a part of JRTPLIB
|
|
|
|
Copyright (c) 1999-2017 Jori Liesenborgs
|
|
|
|
|
|
|
|
Contact: jori.liesenborgs@gmail.com
|
|
|
|
|
|
|
|
This library was developed at the Expertise Centre for Digital Media
|
|
|
|
(http://www.edm.uhasselt.be), a research center of the Hasselt University
|
|
|
|
(http://www.uhasselt.be). The library is based upon work done for
|
|
|
|
my thesis at the School for Knowledge Technology (Belgium/The Netherlands).
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
copy of this software and associated documentation files (the "Software"),
|
|
|
|
to deal in the Software without restriction, including without limitation
|
|
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
|
|
in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
IN THE SOFTWARE.
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
*/
|
2018-02-26 19:35:16 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* \file rtptimeutilities.h
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef RTPTIMEUTILITIES_H
|
|
|
|
|
|
|
|
#define RTPTIMEUTILITIES_H
|
|
|
|
|
|
|
|
#include "rtpconfig.h"
|
|
|
|
#include "rtptypes.h"
|
|
|
|
#ifndef RTP_HAVE_QUERYPERFORMANCECOUNTER
|
2018-02-27 17:05:46 -05:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <errno.h>
|
2018-11-12 10:05:18 -05:00
|
|
|
#else
|
|
|
|
#include "Windows.h"
|
2018-02-26 19:35:16 -05:00
|
|
|
#endif // RTP_HAVE_QUERYPERFORMANCECOUNTER
|
|
|
|
|
2018-03-20 08:49:21 -04:00
|
|
|
#include "export.h"
|
2018-03-03 14:23:38 -05:00
|
|
|
|
2018-02-26 19:35:16 -05:00
|
|
|
#define RTP_NTPTIMEOFFSET 2208988800UL
|
|
|
|
|
|
|
|
#ifdef RTP_HAVE_VSUINT64SUFFIX
|
|
|
|
#define C1000000 1000000ui64
|
|
|
|
#define CEPOCH 11644473600000000ui64
|
|
|
|
#else
|
|
|
|
#define C1000000 1000000ULL
|
|
|
|
#define CEPOCH 11644473600000000ULL
|
|
|
|
#endif // RTP_HAVE_VSUINT64SUFFIX
|
|
|
|
|
2019-05-11 05:14:59 -04:00
|
|
|
#ifdef __APPLE__
|
2019-05-27 10:51:15 -04:00
|
|
|
#include "../custom/apple/apple_compat.h"
|
2019-05-11 05:14:59 -04:00
|
|
|
#endif
|
|
|
|
|
2018-02-26 19:35:16 -05:00
|
|
|
namespace qrtplib
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is a simple wrapper for the most significant word (MSW) and least
|
|
|
|
* significant word (LSW) of an NTP timestamp.
|
|
|
|
*/
|
2018-03-03 14:23:38 -05:00
|
|
|
class QRTPLIB_API RTPNTPTime
|
2018-02-26 19:35:16 -05:00
|
|
|
{
|
|
|
|
public:
|
2018-02-27 17:05:46 -05:00
|
|
|
/** This constructor creates and instance with MSW \c m and LSW \c l. */
|
|
|
|
RTPNTPTime(uint32_t m, uint32_t l)
|
|
|
|
{
|
|
|
|
msw = m;
|
|
|
|
lsw = l;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns the most significant word. */
|
|
|
|
uint32_t GetMSW() const
|
|
|
|
{
|
|
|
|
return msw;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns the least significant word. */
|
|
|
|
uint32_t GetLSW() const
|
|
|
|
{
|
|
|
|
return lsw;
|
|
|
|
}
|
2018-02-26 19:35:16 -05:00
|
|
|
private:
|
2018-02-27 17:05:46 -05:00
|
|
|
uint32_t msw, lsw;
|
2018-02-26 19:35:16 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
/** This class is used to specify wallclock time, delay intervals etc.
|
|
|
|
* This class is used to specify wallclock time, delay intervals etc.
|
|
|
|
* It stores a number of seconds and a number of microseconds.
|
|
|
|
*/
|
2018-03-03 14:23:38 -05:00
|
|
|
class QRTPLIB_API RTPTime
|
2018-02-26 19:35:16 -05:00
|
|
|
{
|
|
|
|
public:
|
2018-02-27 17:05:46 -05:00
|
|
|
/** Returns an RTPTime instance representing the current wallclock time.
|
|
|
|
* Returns an RTPTime instance representing the current wallclock time. This is expressed
|
|
|
|
* as a number of seconds since 00:00:00 UTC, January 1, 1970.
|
|
|
|
*/
|
|
|
|
static RTPTime CurrentTime();
|
|
|
|
|
|
|
|
/** This function waits the amount of time specified in \c delay. */
|
|
|
|
static void Wait(const RTPTime &delay);
|
|
|
|
|
|
|
|
/** Creates an RTPTime instance representing \c t, which is expressed in units of seconds. */
|
|
|
|
RTPTime(double t);
|
|
|
|
|
|
|
|
/** Creates an instance that corresponds to \c ntptime.
|
|
|
|
* Creates an instance that corresponds to \c ntptime. If
|
|
|
|
* the conversion cannot be made, both the seconds and the
|
|
|
|
* microseconds are set to zero.
|
|
|
|
*/
|
|
|
|
RTPTime(RTPNTPTime ntptime);
|
|
|
|
|
|
|
|
/** Creates an instance corresponding to \c seconds and \c microseconds. */
|
|
|
|
RTPTime(int64_t seconds, uint32_t microseconds);
|
|
|
|
|
|
|
|
/** Returns the number of seconds stored in this instance. */
|
|
|
|
int64_t GetSeconds() const;
|
|
|
|
|
|
|
|
/** Returns the number of microseconds stored in this instance. */
|
|
|
|
uint32_t GetMicroSeconds() const;
|
|
|
|
|
|
|
|
/** Returns the time stored in this instance, expressed in units of seconds. */
|
|
|
|
double GetDouble() const
|
|
|
|
{
|
|
|
|
return m_t;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns the NTP time corresponding to the time stored in this instance. */
|
|
|
|
RTPNTPTime GetNTPTime() const;
|
|
|
|
|
|
|
|
RTPTime &operator-=(const RTPTime &t);
|
|
|
|
RTPTime &operator+=(const RTPTime &t);
|
|
|
|
bool operator<(const RTPTime &t) const;
|
|
|
|
bool operator>(const RTPTime &t) const;
|
|
|
|
bool operator<=(const RTPTime &t) const;
|
|
|
|
bool operator>=(const RTPTime &t) const;
|
|
|
|
|
|
|
|
bool IsZero() const
|
|
|
|
{
|
2018-11-12 08:04:16 -05:00
|
|
|
return m_t == 0.0;
|
2018-02-27 17:05:46 -05:00
|
|
|
}
|
2018-02-26 19:35:16 -05:00
|
|
|
private:
|
|
|
|
#ifdef RTP_HAVE_QUERYPERFORMANCECOUNTER
|
2018-02-27 17:05:46 -05:00
|
|
|
static inline uint64_t CalculateMicroseconds(uint64_t performancecount,uint64_t performancefrequency);
|
2018-02-26 19:35:16 -05:00
|
|
|
#endif // RTP_HAVE_QUERYPERFORMANCECOUNTER
|
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
double m_t;
|
2018-02-26 19:35:16 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
inline RTPTime::RTPTime(double t)
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
m_t = t;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline RTPTime::RTPTime(int64_t seconds, uint32_t microseconds)
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
if (seconds >= 0)
|
|
|
|
{
|
|
|
|
m_t = (double) seconds + 1e-6 * (double) microseconds;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int64_t possec = -seconds;
|
|
|
|
|
|
|
|
m_t = (double) possec + 1e-6 * (double) microseconds;
|
|
|
|
m_t = -m_t;
|
|
|
|
}
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline RTPTime::RTPTime(RTPNTPTime ntptime)
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
if (ntptime.GetMSW() < RTP_NTPTIMEOFFSET)
|
|
|
|
{
|
|
|
|
m_t = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
uint32_t sec = ntptime.GetMSW() - RTP_NTPTIMEOFFSET;
|
|
|
|
|
|
|
|
double x = (double) ntptime.GetLSW();
|
|
|
|
x /= (65536.0 * 65536.0);
|
|
|
|
x *= 1000000.0;
|
|
|
|
uint32_t microsec = (uint32_t) x;
|
|
|
|
|
|
|
|
m_t = (double) sec + 1e-6 * (double) microsec;
|
|
|
|
}
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline int64_t RTPTime::GetSeconds() const
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
return (int64_t) m_t;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline uint32_t RTPTime::GetMicroSeconds() const
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
uint32_t microsec;
|
|
|
|
|
|
|
|
if (m_t >= 0)
|
|
|
|
{
|
|
|
|
int64_t sec = (int64_t) m_t;
|
|
|
|
microsec = (uint32_t) (1e6 * (m_t - (double) sec) + 0.5);
|
|
|
|
}
|
|
|
|
else // m_t < 0
|
|
|
|
{
|
|
|
|
int64_t sec = (int64_t) (-m_t);
|
|
|
|
microsec = (uint32_t) (1e6 * ((-m_t) - (double) sec) + 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (microsec >= 1000000)
|
|
|
|
return 999999;
|
|
|
|
// Unsigned, it can never be less than 0
|
|
|
|
// if (microsec < 0)
|
|
|
|
// return 0;
|
|
|
|
return microsec;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef RTP_HAVE_QUERYPERFORMANCECOUNTER
|
|
|
|
|
|
|
|
inline uint64_t RTPTime::CalculateMicroseconds(uint64_t performancecount,uint64_t performancefrequency)
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
uint64_t f = performancefrequency;
|
|
|
|
uint64_t a = performancecount;
|
|
|
|
uint64_t b = a/f;
|
|
|
|
uint64_t c = a%f; // a = b*f+c => (a*1000000)/f = b*1000000+(c*1000000)/f
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
return b*C1000000+(c*C1000000)/f;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline RTPTime RTPTime::CurrentTime()
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
static int inited = 0;
|
|
|
|
static uint64_t microseconds, initmicroseconds;
|
|
|
|
static LARGE_INTEGER performancefrequency;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
uint64_t emulate_microseconds, microdiff;
|
|
|
|
SYSTEMTIME systemtime;
|
|
|
|
FILETIME filetime;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
LARGE_INTEGER performancecount;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
QueryPerformanceCounter(&performancecount);
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
if(!inited)
|
|
|
|
{
|
|
|
|
inited = 1;
|
|
|
|
QueryPerformanceFrequency(&performancefrequency);
|
|
|
|
GetSystemTime(&systemtime);
|
|
|
|
SystemTimeToFileTime(&systemtime,&filetime);
|
|
|
|
microseconds = ( ((uint64_t)(filetime.dwHighDateTime) << 32) + (uint64_t)(filetime.dwLowDateTime) ) / (uint64_t)10;
|
|
|
|
microseconds-= CEPOCH; // EPOCH
|
|
|
|
initmicroseconds = CalculateMicroseconds(performancecount.QuadPart, performancefrequency.QuadPart);
|
|
|
|
}
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
emulate_microseconds = CalculateMicroseconds(performancecount.QuadPart, performancefrequency.QuadPart);
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
microdiff = emulate_microseconds - initmicroseconds;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
double t = 1e-6*(double)(microseconds + microdiff);
|
|
|
|
return RTPTime(t);
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void RTPTime::Wait(const RTPTime &delay)
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
if (delay.m_t <= 0)
|
|
|
|
return;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
uint64_t sec = (uint64_t)delay.m_t;
|
|
|
|
uint32_t microsec = (uint32_t)(1e6*(delay.m_t-(double)sec));
|
|
|
|
DWORD t = ((DWORD)sec)*1000+(((DWORD)microsec)/1000);
|
|
|
|
Sleep(t);
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#else // unix style
|
|
|
|
|
|
|
|
#ifdef RTP_HAVE_CLOCK_GETTIME
|
|
|
|
inline double RTPTime_timespecToDouble(struct timespec &ts)
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
return (double) ts.tv_sec + 1e-9 * (double) ts.tv_nsec;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline RTPTime RTPTime::CurrentTime()
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
static bool s_initialized = false;
|
|
|
|
static double s_startOffet = 0;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
if (!s_initialized)
|
|
|
|
{
|
|
|
|
s_initialized = true;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
// Get the corresponding times in system time and monotonic time
|
|
|
|
struct timespec tpSys, tpMono;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
clock_gettime(CLOCK_REALTIME, &tpSys);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &tpMono);
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
double tSys = RTPTime_timespecToDouble(tpSys);
|
|
|
|
double tMono = RTPTime_timespecToDouble(tpMono);
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
s_startOffet = tSys - tMono;
|
|
|
|
return tSys;
|
|
|
|
}
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
struct timespec tpMono;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &tpMono);
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
double tMono0 = RTPTime_timespecToDouble(tpMono);
|
|
|
|
return tMono0 + s_startOffet;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#else // gettimeofday fallback
|
|
|
|
|
|
|
|
inline RTPTime RTPTime::CurrentTime()
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
struct timeval tv;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
gettimeofday(&tv,0);
|
|
|
|
return RTPTime((uint64_t)tv.tv_sec,(uint32_t)tv.tv_usec);
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
#endif // RTP_HAVE_CLOCK_GETTIME
|
|
|
|
|
|
|
|
inline void RTPTime::Wait(const RTPTime &delay)
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
if (delay.m_t <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
uint64_t sec = (uint64_t) delay.m_t;
|
|
|
|
uint64_t nanosec = (uint32_t) (1e9 * (delay.m_t - (double) sec));
|
|
|
|
|
|
|
|
struct timespec req, rem;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
req.tv_sec = (time_t) sec;
|
|
|
|
req.tv_nsec = ((long) nanosec);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ret = nanosleep(&req, &rem);
|
|
|
|
req = rem;
|
|
|
|
} while (ret == -1 && errno == EINTR);
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif // RTP_HAVE_QUERYPERFORMANCECOUNTER
|
|
|
|
|
|
|
|
inline RTPTime &RTPTime::operator-=(const RTPTime &t)
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
m_t -= t.m_t;
|
|
|
|
return *this;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline RTPTime &RTPTime::operator+=(const RTPTime &t)
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
m_t += t.m_t;
|
|
|
|
return *this;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline RTPNTPTime RTPTime::GetNTPTime() const
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
uint32_t sec = (uint32_t) m_t;
|
|
|
|
uint32_t microsec = (uint32_t) ((m_t - (double) sec) * 1e6);
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
uint32_t msw = sec + RTP_NTPTIMEOFFSET;
|
|
|
|
uint32_t lsw;
|
|
|
|
double x;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
x = microsec / 1000000.0;
|
|
|
|
x *= (65536.0 * 65536.0);
|
|
|
|
lsw = (uint32_t) x;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
2018-02-27 17:05:46 -05:00
|
|
|
return RTPNTPTime(msw, lsw);
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool RTPTime::operator<(const RTPTime &t) const
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
return m_t < t.m_t;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool RTPTime::operator>(const RTPTime &t) const
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
return m_t > t.m_t;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool RTPTime::operator<=(const RTPTime &t) const
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
return m_t <= t.m_t;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool RTPTime::operator>=(const RTPTime &t) const
|
|
|
|
{
|
2018-02-27 17:05:46 -05:00
|
|
|
return m_t >= t.m_t;
|
2018-02-26 19:35:16 -05:00
|
|
|
}
|
|
|
|
|
2018-03-03 14:23:38 -05:00
|
|
|
class QRTPLIB_API RTPTimeInitializerObject
|
2018-02-26 19:35:16 -05:00
|
|
|
{
|
|
|
|
public:
|
2018-02-27 17:05:46 -05:00
|
|
|
RTPTimeInitializerObject();
|
|
|
|
void Dummy()
|
|
|
|
{
|
|
|
|
dummy++;
|
|
|
|
}
|
2018-02-26 19:35:16 -05:00
|
|
|
private:
|
2018-02-27 17:05:46 -05:00
|
|
|
int dummy;
|
2018-02-26 19:35:16 -05:00
|
|
|
};
|
|
|
|
|
2018-03-03 14:23:38 -05:00
|
|
|
extern QRTPLIB_API RTPTimeInitializerObject timeinit;
|
2018-02-26 19:35:16 -05:00
|
|
|
|
|
|
|
} // end namespace
|
|
|
|
|
|
|
|
#endif // RTPTIMEUTILITIES_H
|
|
|
|
|