mtime.c 13.1 KB
Newer Older
1
/*****************************************************************************
2
 * mtime.c: high resolution time management functions
Clément Stenac's avatar
Clément Stenac committed
3
 * Functions are prototyped in vlc_mtime.h.
4
 *****************************************************************************
5
 * Copyright (C) 1998-2007 the VideoLAN team
6
 * Copyright © 2006-2007 Rémi Denis-Courmont
7
 * $Id$
8
 *
9
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
10
 *          Rémi Denis-Courmont <rem$videolan,org>
11
 *          Gisle Vanem
12 13 14 15 16
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
Clément Stenac's avatar
Clément Stenac committed
17
 *
18 19
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
22
 *
23 24
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
27

28
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
29
 * Preamble
30
 *****************************************************************************/
31 32 33

#include <vlc/vlc.h>

34
#include <stdio.h>                                              /* sprintf() */
35
#include <time.h>                      /* clock_gettime(), clock_nanosleep() */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
36
#include <stdlib.h>                                               /* lldiv() */
37
#include <assert.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
38

Sam Hocevar's avatar
 
Sam Hocevar committed
39

Sam Hocevar's avatar
 
Sam Hocevar committed
40 41 42 43
#if defined( PTH_INIT_IN_PTH_H )                                  /* GNU Pth */
#   include <pth.h>
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
44
#ifdef HAVE_UNISTD_H
Sam Hocevar's avatar
 
Sam Hocevar committed
45
#   include <unistd.h>                                           /* select() */
Sam Hocevar's avatar
 
Sam Hocevar committed
46
#endif
Michel Kaempf's avatar
Michel Kaempf committed
47

48
#ifdef HAVE_KERNEL_OS_H
Sam Hocevar's avatar
 
Sam Hocevar committed
49
#   include <kernel/OS.h>
50 51
#endif

52
#if defined( WIN32 ) || defined( UNDER_CE )
Sam Hocevar's avatar
 
Sam Hocevar committed
53 54 55
#   include <windows.h>
#else
#   include <sys/time.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
56 57
#endif

58
#if !defined(HAVE_STRUCT_TIMESPEC)
59 60
struct timespec
{
61 62
    time_t  tv_sec;
    int32_t tv_nsec;
63 64 65 66 67 68 69
};
#endif

#if defined(HAVE_NANOSLEEP) && !defined(HAVE_DECL_NANOSLEEP)
int nanosleep(struct timespec *, struct timespec *);
#endif

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
70 71 72
/**
 * Return a date in a readable format
 *
73 74
 * This function converts a mtime date into a string.
 * psz_buffer should be a buffer long enough to store the formatted
Clément Stenac's avatar
Clément Stenac committed
75
 * date.
76 77 78 79
 * \param date to be converted
 * \param psz_buffer should be a buffer at least MSTRTIME_MAX_SIZE characters
 * \return psz_buffer is returned so this can be used as printf parameter.
 */
Michel Kaempf's avatar
Michel Kaempf committed
80 81
char *mstrtime( char *psz_buffer, mtime_t date )
{
82 83
    static mtime_t ll1000 = 1000, ll60 = 60, ll24 = 24;

84
    snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02d:%02d:%02d-%03d.%03d",
85 86 87 88 89
             (int) (date / (ll1000 * ll1000 * ll60 * ll60) % ll24),
             (int) (date / (ll1000 * ll1000 * ll60) % ll60),
             (int) (date / (ll1000 * ll1000) % ll60),
             (int) (date / ll1000 % ll1000),
             (int) (date % ll1000) );
Michel Kaempf's avatar
Michel Kaempf committed
90 91 92
    return( psz_buffer );
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
93 94 95
/**
 * Convert seconds to a time in the format h:mm:ss.
 *
96 97 98 99 100 101 102
 * This function is provided for any interface function which need to print a
 * time string in the format h:mm:ss
 * date.
 * \param secs  the date to be converted
 * \param psz_buffer should be a buffer at least MSTRTIME_MAX_SIZE characters
 * \return psz_buffer is returned so this can be used as printf parameter.
 */
103
char *secstotimestr( char *psz_buffer, int i_seconds )
104
{
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
105
    snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%d:%2.2d:%2.2d",
Clément Stenac's avatar
Clément Stenac committed
106 107
              (int) (i_seconds / (60 *60)),
              (int) ((i_seconds / 60) % 60),
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
108 109
              (int) (i_seconds % 60) );
    return( psz_buffer );
110 111
}

112

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
113 114 115
/**
 * Return high precision date
 *
Michel Kaempf's avatar
Michel Kaempf committed
116 117
 * Uses the gettimeofday() function when possible (1 MHz resolution) or the
 * ftime() function (1 kHz resolution).
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
118
 */
Michel Kaempf's avatar
Michel Kaempf committed
119
mtime_t mdate( void )
Michel Kaempf's avatar
Michel Kaempf committed
120
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
121 122 123 124 125 126 127 128 129 130 131 132 133 134
#if defined (HAVE_CLOCK_NANOSLEEP)
    struct timespec ts;

# if (_POSIX_MONOTONIC_CLOCK - 0 >= 0)
    /* Try to use POSIX monotonic clock if available */
    if( clock_gettime( CLOCK_MONOTONIC, &ts ) )
# endif
        /* Run-time fallback to real-time clock (always available) */
        (void)clock_gettime( CLOCK_REALTIME, &ts );

    return ((mtime_t)ts.tv_sec * (mtime_t)1000000)
           + (mtime_t)(ts.tv_nsec / 1000);

#elif defined( HAVE_KERNEL_OS_H )
135
    return( real_time_clock_usecs() );
Sam Hocevar's avatar
 
Sam Hocevar committed
136

137
#elif defined( WIN32 ) || defined( UNDER_CE )
Gildas Bazin's avatar
 
Gildas Bazin committed
138 139
    /* We don't need the real date, just the value of a high precision timer */
    static mtime_t freq = I64C(-1);
Sam Hocevar's avatar
 
Sam Hocevar committed
140

Gildas Bazin's avatar
 
Gildas Bazin committed
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    if( freq == I64C(-1) )
    {
        /* Extract from the Tcl source code:
         * (http://www.cs.man.ac.uk/fellowsd-bin/TIP/7.html)
         *
         * Some hardware abstraction layers use the CPU clock
         * in place of the real-time clock as a performance counter
         * reference.  This results in:
         *    - inconsistent results among the processors on
         *      multi-processor systems.
         *    - unpredictable changes in performance counter frequency
         *      on "gearshift" processors such as Transmeta and
         *      SpeedStep.
         * There seems to be no way to test whether the performance
         * counter is reliable, but a useful heuristic is that
         * if its frequency is 1.193182 MHz or 3.579545 MHz, it's
         * derived from a colorburst crystal and is therefore
         * the RTC rather than the TSC.  If it's anything else, we
         * presume that the performance counter is unreliable.
         */
161
        LARGE_INTEGER buf;
Gildas Bazin's avatar
 
Gildas Bazin committed
162

163
        freq = ( QueryPerformanceFrequency( &buf ) &&
164
                 (buf.QuadPart == I64C(1193182) || buf.QuadPart == I64C(3579545) ) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
165
               ? buf.QuadPart : 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
166 167 168
    }

    if( freq != 0 )
Sam Hocevar's avatar
 
Sam Hocevar committed
169
    {
170 171 172 173 174 175 176 177 178
        LARGE_INTEGER counter;
        QueryPerformanceCounter (&counter);

        /* Convert to from (1/freq) to microsecond resolution */
        /* We need to split the division to avoid 63-bits overflow */
        lldiv_t d = lldiv (counter.QuadPart, freq);

        return (d.quot * 1000000)
             + ((d.rem * 1000000) / freq);
Sam Hocevar's avatar
 
Sam Hocevar committed
179
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
180 181 182 183 184 185 186 187 188 189
    else
    {
        /* Fallback on GetTickCount() which has a milisecond resolution
         * (actually, best case is about 10 ms resolution)
         * GetTickCount() only returns a DWORD thus will wrap after
         * about 49.7 days so we try to detect the wrapping. */

        static CRITICAL_SECTION date_lock;
        static mtime_t i_previous_time = I64C(-1);
        static int i_wrap_counts = -1;
190
        mtime_t usec_time;
Gildas Bazin's avatar
 
Gildas Bazin committed
191 192 193 194

        if( i_wrap_counts == -1 )
        {
            /* Initialization */
195
            i_previous_time = I64C(1000) * GetTickCount();
Gildas Bazin's avatar
 
Gildas Bazin committed
196 197 198 199 200 201 202 203 204 205 206
            InitializeCriticalSection( &date_lock );
            i_wrap_counts = 0;
        }

        EnterCriticalSection( &date_lock );
        usec_time = I64C(1000) *
            (i_wrap_counts * I64C(0x100000000) + GetTickCount());
        if( i_previous_time > usec_time )
        {
            /* Counter wrapped */
            i_wrap_counts++;
207
            usec_time += I64C(0x100000000) * 1000;
Gildas Bazin's avatar
 
Gildas Bazin committed
208 209 210 211 212 213
        }
        i_previous_time = usec_time;
        LeaveCriticalSection( &date_lock );

        return usec_time;
    }
214
#else
Michel Kaempf's avatar
Michel Kaempf committed
215 216
    struct timeval tv_date;

217 218
    /* gettimeofday() cannot fail given &tv_date is a valid address */
    (void)gettimeofday( &tv_date, NULL );
Michel Kaempf's avatar
Michel Kaempf committed
219
    return( (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec );
220
#endif
Michel Kaempf's avatar
Michel Kaempf committed
221 222
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
223 224 225
/**
 * Wait for a date
 *
Michel Kaempf's avatar
Michel Kaempf committed
226 227 228
 * This function uses select() and an system date function to wake up at a
 * precise date. It should be used for process synchronization. If current date
 * is posterior to wished date, the function returns immediately.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
229 230
 * \param date The date to wake up at
 */
Michel Kaempf's avatar
Michel Kaempf committed
231 232
void mwait( mtime_t date )
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
233
#if defined (HAVE_CLOCK_NANOSLEEP)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
234
    lldiv_t d = lldiv( date, 1000000 );
235
    struct timespec ts = { d.quot, d.rem * 1000 };
236

237
# if (_POSIX_MONOTONIC_CLOCK - 0 >= 0)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
238
    if( clock_nanosleep( CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL ) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
239
# endif
240
        clock_nanosleep( CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL );
241 242
#else

243
    mtime_t delay = date - mdate();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
244 245 246
    if( delay > 0 )
        msleep( delay );

Sam Hocevar's avatar
 
Sam Hocevar committed
247
#endif
Michel Kaempf's avatar
Michel Kaempf committed
248 249
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
250 251 252
/**
 * More precise sleep()
 *
253
 * Portable usleep() function.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
254 255
 * \param delay the amount of time to sleep
 */
Michel Kaempf's avatar
Michel Kaempf committed
256 257
void msleep( mtime_t delay )
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
258 259 260 261 262 263 264 265 266 267
#if defined( HAVE_CLOCK_NANOSLEEP ) 
    lldiv_t d = lldiv( delay, 1000000 );
    struct timespec ts = { d.quot, d.rem * 1000 };

# if (_POSIX_MONOTONIC_CLOCK - 0 >= 0)
    if( clock_nanosleep( CLOCK_MONOTONIC, 0, &ts, NULL ) )
# endif
        clock_nanosleep( CLOCK_REALTIME, 0, &ts, NULL );

#elif defined( HAVE_KERNEL_OS_H )
268 269
    snooze( delay );

Sam Hocevar's avatar
 
Sam Hocevar committed
270
#elif defined( PTH_INIT_IN_PTH_H )
Sam Hocevar's avatar
 
Sam Hocevar committed
271 272 273 274
    pth_usleep( delay );

#elif defined( ST_INIT_IN_ST_H )
    st_usleep( delay );
Sam Hocevar's avatar
 
Sam Hocevar committed
275

276
#elif defined( WIN32 ) || defined( UNDER_CE )
Gildas Bazin's avatar
 
Gildas Bazin committed
277 278
    Sleep( (int) (delay / 1000) );

279 280 281 282 283 284 285 286
#elif defined( HAVE_NANOSLEEP )
    struct timespec ts_delay;

    ts_delay.tv_sec = delay / 1000000;
    ts_delay.tv_nsec = (delay % 1000000) * 1000;

    nanosleep( &ts_delay, NULL );

287
#else
Michel Kaempf's avatar
Michel Kaempf committed
288 289 290 291
    struct timeval tv_delay;

    tv_delay.tv_sec = delay / 1000000;
    tv_delay.tv_usec = delay % 1000000;
292

Michel Kaempf's avatar
Michel Kaempf committed
293 294 295
    /* select() return value should be tested, since several possible errors
     * can occur. However, they should only happen in very particular occasions
     * (i.e. when a signal is sent to the thread, or when memory is full), and
296
     * can be ignored. */
Michel Kaempf's avatar
Michel Kaempf committed
297
    select( 0, NULL, NULL, NULL, &tv_delay );
Sam Hocevar's avatar
 
Sam Hocevar committed
298
#endif
Michel Kaempf's avatar
Michel Kaempf committed
299
}
Sam Hocevar's avatar
 
Sam Hocevar committed
300

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
/*
 * Date management (internal and external)
 */

/**
 * Initialize a date_t.
 *
 * \param date to initialize
 * \param divider (sample rate) numerator
 * \param divider (sample rate) denominator
 */

void date_Init( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d )
{
    p_date->date = 0;
    p_date->i_divider_num = i_divider_n;
    p_date->i_divider_den = i_divider_d;
    p_date->i_remainder = 0;
}

/**
 * Change a date_t.
 *
 * \param date to change
 * \param divider (sample rate) numerator
 * \param divider (sample rate) denominator
 */

void date_Change( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d )
{
    p_date->i_divider_num = i_divider_n;
    p_date->i_divider_den = i_divider_d;
}

/**
 * Set the date value of a date_t.
 *
 * \param date to set
 * \param date value
 */
void date_Set( date_t *p_date, mtime_t i_new_date )
{
    p_date->date = i_new_date;
    p_date->i_remainder = 0;
}

/**
 * Get the date of a date_t
 *
 * \param date to get
 * \return date value
 */
mtime_t date_Get( const date_t *p_date )
{
    return p_date->date;
}

/**
 * Move forwards or backwards the date of a date_t.
 *
 * \param date to move
 * \param difference value
 */
void date_Move( date_t *p_date, mtime_t i_difference )
{
    p_date->date += i_difference;
}

/**
 * Increment the date and return the result, taking into account
 * rounding errors.
 *
 * \param date to increment
 * \param incrementation in number of samples
 * \return date value
 */
mtime_t date_Increment( date_t *p_date, uint32_t i_nb_samples )
{
    mtime_t i_dividend = (mtime_t)i_nb_samples * 1000000;
    p_date->date += i_dividend / p_date->i_divider_num * p_date->i_divider_den;
    p_date->i_remainder += (int)(i_dividend % p_date->i_divider_num);

    if( p_date->i_remainder >= p_date->i_divider_num )
    {
        /* This is Bresenham algorithm. */
        p_date->date += p_date->i_divider_den;
        p_date->i_remainder -= p_date->i_divider_num;
    }

    return p_date->date;
}
392

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
#ifdef WIN32
/*
 * Number of micro-seconds between the beginning of the Windows epoch
 * (Jan. 1, 1601) and the Unix epoch (Jan. 1, 1970).
 *
 * This assumes all Win32 compilers have 64-bit support.
 */
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) || defined(__WATCOMC__)
#   define DELTA_EPOCH_IN_USEC  11644473600000000Ui64
#else
#   define DELTA_EPOCH_IN_USEC  11644473600000000ULL
#endif

static uint64_t filetime_to_unix_epoch (const FILETIME *ft)
{
    uint64_t res = (uint64_t) ft->dwHighDateTime << 32;

    res |= ft->dwLowDateTime;
    res /= 10;                   /* from 100 nano-sec periods to usec */
    res -= DELTA_EPOCH_IN_USEC;  /* from Win epoch to Unix epoch */
    return (res);
}

static int gettimeofday (struct timeval *tv, void *tz )
{
    FILETIME  ft;
    uint64_t tim;

    if (!tv) {
        return VLC_EGENERIC;
    }
    GetSystemTimeAsFileTime (&ft);
    tim = filetime_to_unix_epoch (&ft);
    tv->tv_sec  = (long) (tim / 1000000L);
    tv->tv_usec = (long) (tim % 1000000L);
    return (0);
}
#endif



434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
/**
 * @return NTP 64-bits timestamp in host byte order.
 */
uint64_t NTPtime64 (void)
{
    struct timespec ts;
#if defined (CLOCK_REALTIME)
    clock_gettime (CLOCK_REALTIME, &ts);
#else
    {
        struct timeval tv;
        gettimeofday (&tv, NULL);
        ts.tv_sec = tv.tv_sec;
        ts.tv_nsec = tv.tv_usec * 1000;
    }
#endif

    /* Convert nanoseconds to 32-bits fraction (232 picosecond units) */
    uint64_t t = (uint64_t)(ts.tv_nsec) << 32;
    t /= 1000000000;


    /* There is 70 years (incl. 17 leap ones) offset to the Unix Epoch.
     * No leap seconds during that period since they were not invented yet.
     */
    assert (t < 0x100000000);
    t |= ((70LL * 365 + 17) * 24 * 60 * 60 + ts.tv_sec) << 32;
    return t;
}