mtime.c 12.5 KB
Newer Older
1
/*****************************************************************************
2
 * mtime.c: high resolution time management functions
Michel Kaempf's avatar
Michel Kaempf committed
3
 * Functions are prototyped in mtime.h.
4
 *****************************************************************************
5
 * Copyright (C) 1998-2004 the VideoLAN team
6
 * $Id$
7
 *
8
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
9
 *          Rémi Denis-Courmont <rem$videolan,org>
10 11 12 13 14
 *
 * 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
15
 *
16 17
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
20
 *
21 22
 * 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
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
25

26
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
27
 * Preamble
28
 *****************************************************************************/
29
#include <stdio.h>                                              /* sprintf() */
30 31
#include <time.h>                      /* clock_gettime(), clock_nanosleep() */
#include <stdlib.h>                                                /* ldiv() */
Sam Hocevar's avatar
 
Sam Hocevar committed
32

33
#include <vlc/vlc.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
34

Sam Hocevar's avatar
 
Sam Hocevar committed
35 36 37 38
#if defined( PTH_INIT_IN_PTH_H )                                  /* GNU Pth */
#   include <pth.h>
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
39
#ifdef HAVE_UNISTD_H
Sam Hocevar's avatar
 
Sam Hocevar committed
40
#   include <unistd.h>                                           /* select() */
Sam Hocevar's avatar
 
Sam Hocevar committed
41
#endif
Michel Kaempf's avatar
Michel Kaempf committed
42

43
#ifdef HAVE_KERNEL_OS_H
Sam Hocevar's avatar
 
Sam Hocevar committed
44
#   include <kernel/OS.h>
45 46
#endif

47
#if defined( WIN32 ) || defined( UNDER_CE )
Sam Hocevar's avatar
 
Sam Hocevar committed
48 49 50
#   include <windows.h>
#else
#   include <sys/time.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
51 52
#endif

53 54 55
#if defined(HAVE_NANOSLEEP) && !defined(HAVE_STRUCT_TIMESPEC)
struct timespec
{
56 57
    time_t  tv_sec;
    int32_t tv_nsec;
58 59 60 61 62 63 64
};
#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
65 66 67
/**
 * Return a date in a readable format
 *
68 69
 * 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
70
 * date.
71 72 73 74
 * \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
75 76
char *mstrtime( char *psz_buffer, mtime_t date )
{
77 78
    static mtime_t ll1000 = 1000, ll60 = 60, ll24 = 24;

79
    snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02d:%02d:%02d-%03d.%03d",
80 81 82 83 84
             (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
85 86 87
    return( psz_buffer );
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
88 89 90
/**
 * Convert seconds to a time in the format h:mm:ss.
 *
91 92 93 94 95 96 97
 * 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.
 */
98
char *secstotimestr( char *psz_buffer, int i_seconds )
99
{
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
100
    snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%d:%2.2d:%2.2d",
Clément Stenac's avatar
Clément Stenac committed
101 102
              (int) (i_seconds / (60 *60)),
              (int) ((i_seconds / 60) % 60),
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
103 104
              (int) (i_seconds % 60) );
    return( psz_buffer );
105 106
}

107

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
108 109 110
/**
 * Return high precision date
 *
Michel Kaempf's avatar
Michel Kaempf committed
111 112
 * 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
113
 */
Michel Kaempf's avatar
Michel Kaempf committed
114
mtime_t mdate( void )
Michel Kaempf's avatar
Michel Kaempf committed
115
{
Sam Hocevar's avatar
 
Sam Hocevar committed
116
#if defined( HAVE_KERNEL_OS_H )
117
    return( real_time_clock_usecs() );
Sam Hocevar's avatar
 
Sam Hocevar committed
118

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

Gildas Bazin's avatar
 
Gildas Bazin committed
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    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.
         */

        freq = ( QueryPerformanceFrequency( (LARGE_INTEGER *)&freq ) &&
                 (freq == I64C(1193182) || freq == I64C(3579545) ) )
               ? freq : 0;
    }

    if( freq != 0 )
Sam Hocevar's avatar
 
Sam Hocevar committed
151
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
152 153
        /* Microsecond resolution */
        QueryPerformanceCounter( (LARGE_INTEGER *)&usec_time );
Gildas Bazin's avatar
 
Gildas Bazin committed
154
        return ( usec_time * 1000000 ) / freq;
Sam Hocevar's avatar
 
Sam Hocevar committed
155
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
156 157 158 159 160 161 162 163 164 165 166 167 168 169
    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;

        if( i_wrap_counts == -1 )
        {
            /* Initialization */
170
            i_previous_time = I64C(1000) * GetTickCount();
Gildas Bazin's avatar
 
Gildas Bazin committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
            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++;
            usec_time += I64C(0x100000000000);
        }
        i_previous_time = usec_time;
        LeaveCriticalSection( &date_lock );

        return usec_time;
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
189

190 191 192 193 194 195 196 197 198 199 200
#elif defined (HAVE_CLOCK_GETTIME)
    struct timespec ts;

# ifdef _POSIX_MONOTONIC_CLOCK
    /* 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 (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
201
#else
Michel Kaempf's avatar
Michel Kaempf committed
202 203
    struct timeval tv_date;

204 205
    /* gettimeofday() cannot fail given &tv_date is a valid address */
    (void)gettimeofday( &tv_date, NULL );
Michel Kaempf's avatar
Michel Kaempf committed
206
    return( (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec );
207
#endif
Michel Kaempf's avatar
Michel Kaempf committed
208 209
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
210 211 212
/**
 * Wait for a date
 *
Michel Kaempf's avatar
Michel Kaempf committed
213 214 215
 * 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
216 217
 * \param date The date to wake up at
 */
Michel Kaempf's avatar
Michel Kaempf committed
218 219
void mwait( mtime_t date )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
220
#if defined( HAVE_KERNEL_OS_H )
221
    mtime_t delay;
Clément Stenac's avatar
Clément Stenac committed
222

223 224 225 226 227 228 229
    delay = date - real_time_clock_usecs();
    if( delay <= 0 )
    {
        return;
    }
    snooze( delay );

230
#elif defined( WIN32 ) || defined( UNDER_CE )
Sam Hocevar's avatar
 
Sam Hocevar committed
231 232 233 234 235 236 237 238
    mtime_t usec_time, delay;

    usec_time = mdate();
    delay = date - usec_time;
    if( delay <= 0 )
    {
        return;
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
239
    msleep( delay );
240

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
#elif defined (HAVE_CLOCK_GETTIME)
    struct timespec ts;
    ldiv_t d;

# if 1
    /*
     * Ideally, we'd use absolute time (TIMER_ABSTIME), instead of
     * computing the time difference... but VLC mtime_t type seems to
     * overflow way too quickly for this to work properly, or maybe it's a
     * signedness problem (??).
     */
    date -= mdate();
    if( date <= 0 )
        return;
# endif
    d = ldiv( date, 1000000 );
    ts.tv_sec = d.quot;
    ts.tv_nsec = d.rem * 1000;

# ifdef _POSIX_MONOTONIC_CLOCK
    if( clock_nanosleep( CLOCK_MONOTONIC, 0 /*TIMER_ABSTIME*/, &ts, NULL ) )
# endif
        (void)clock_nanosleep( CLOCK_REALTIME, 0, &ts, NULL );
264 265
#else

266
    struct timeval tv_date;
267
    mtime_t        delay;          /* delay in msec, signed to detect errors */
Michel Kaempf's avatar
Michel Kaempf committed
268 269 270 271 272

    /* see mdate() about gettimeofday() possible errors */
    gettimeofday( &tv_date, NULL );

    /* calculate delay and check if current date is before wished date */
Sam Hocevar's avatar
 
Sam Hocevar committed
273 274 275 276
    delay = date - (mtime_t) tv_date.tv_sec * 1000000
                 - (mtime_t) tv_date.tv_usec
                 - 10000;

277 278
    /* Linux/i386 has a granularity of 10 ms. It's better to be in advance
     * than to be late. */
279
    if( delay <= 0 )                 /* wished date is now or already passed */
Michel Kaempf's avatar
Michel Kaempf committed
280 281 282
    {
        return;
    }
283

Sam Hocevar's avatar
 
Sam Hocevar committed
284 285 286
#   if defined( PTH_INIT_IN_PTH_H )
    pth_usleep( delay );

Sam Hocevar's avatar
 
Sam Hocevar committed
287 288 289
#   elif defined( ST_INIT_IN_ST_H )
    st_usleep( delay );

Sam Hocevar's avatar
 
Sam Hocevar committed
290
#   else
291 292 293 294 295 296 297 298 299 300 301 302 303

#       if defined( HAVE_NANOSLEEP )
    {
        struct timespec ts_delay;
        ts_delay.tv_sec = delay / 1000000;
        ts_delay.tv_nsec = (delay % 1000000) * 1000;

        nanosleep( &ts_delay, NULL );
    }

#       else
    tv_date.tv_sec = delay / 1000000;
    tv_date.tv_usec = delay % 1000000;
Michel Kaempf's avatar
Michel Kaempf committed
304
    /* see msleep() about select() errors */
305 306
    select( 0, NULL, NULL, NULL, &tv_date );
#       endif
Sam Hocevar's avatar
 
Sam Hocevar committed
307

Sam Hocevar's avatar
 
Sam Hocevar committed
308
#   endif
309

Sam Hocevar's avatar
 
Sam Hocevar committed
310
#endif
Michel Kaempf's avatar
Michel Kaempf committed
311 312
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
313 314 315
/**
 * More precise sleep()
 *
316
 * Portable usleep() function.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
317 318
 * \param delay the amount of time to sleep
 */
Michel Kaempf's avatar
Michel Kaempf committed
319 320
void msleep( mtime_t delay )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
321
#if defined( HAVE_KERNEL_OS_H )
322 323
    snooze( delay );

Sam Hocevar's avatar
 
Sam Hocevar committed
324
#elif defined( PTH_INIT_IN_PTH_H )
Sam Hocevar's avatar
 
Sam Hocevar committed
325 326 327 328
    pth_usleep( delay );

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

330
#elif defined( WIN32 ) || defined( UNDER_CE )
Gildas Bazin's avatar
 
Gildas Bazin committed
331 332
    Sleep( (int) (delay / 1000) );

333 334 335 336 337 338 339 340
#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 );

341
#else
Michel Kaempf's avatar
Michel Kaempf committed
342 343 344 345
    struct timeval tv_delay;

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

Michel Kaempf's avatar
Michel Kaempf committed
347 348 349
    /* 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
350
     * can be ignored. */
Michel Kaempf's avatar
Michel Kaempf committed
351
    select( 0, NULL, NULL, NULL, &tv_delay );
352

Sam Hocevar's avatar
 
Sam Hocevar committed
353
#endif
Michel Kaempf's avatar
Michel Kaempf committed
354
}
Sam Hocevar's avatar
 
Sam Hocevar committed
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 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 434 435 436 437 438 439 440 441 442 443 444 445 446
/*
 * 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;
}