X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fmtime.c;h=46939dc9bc159c2e8d0a02d962cd04e36fe61372;hb=fe2a7b4912614bad4a46809bb319d3f7ec68a2a4;hp=7a5ab5e7b81c224217029e90c8a7391e56501574;hpb=d3fe7f28797d4dba65ffcdd60bf932e758a48a9e;p=vlc diff --git a/src/misc/mtime.c b/src/misc/mtime.c index 7a5ab5e7b8..46939dc9bc 100644 --- a/src/misc/mtime.c +++ b/src/misc/mtime.c @@ -2,66 +2,48 @@ * mtime.c: high resolution time management functions * Functions are prototyped in vlc_mtime.h. ***************************************************************************** - * Copyright (C) 1998-2004 the VideoLAN team + * Copyright (C) 1998-2007 VLC authors and VideoLAN + * Copyright © 2006-2007 Rémi Denis-Courmont * $Id$ * * Authors: Vincent Seguin * Rémi Denis-Courmont + * Gisle Vanem * - * 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 + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include - -#include /* sprintf() */ -#include /* clock_gettime(), clock_nanosleep() */ -#include /* lldiv() */ - - -#if defined( PTH_INIT_IN_PTH_H ) /* GNU Pth */ -# include +#ifdef HAVE_CONFIG_H +# include "config.h" #endif -#ifdef HAVE_UNISTD_H -# include /* select() */ -#endif +#include +#include -#ifdef HAVE_KERNEL_OS_H -# include +#include +#if !defined (_POSIX_TIMERS) || defined (_WIN32) +# define _POSIX_TIMERS (-1) #endif - -#if defined( WIN32 ) || defined( UNDER_CE ) -# include +#if (_POSIX_TIMERS > 0) +# include /* clock_gettime() */ #else -# include -#endif - -#if defined(HAVE_NANOSLEEP) && !defined(HAVE_STRUCT_TIMESPEC) -struct timespec -{ - time_t tv_sec; - int32_t tv_nsec; -}; -#endif - -#if defined(HAVE_NANOSLEEP) && !defined(HAVE_DECL_NANOSLEEP) -int nanosleep(struct timespec *, struct timespec *); +# include #endif /** @@ -76,7 +58,7 @@ int nanosleep(struct timespec *, struct timespec *); */ char *mstrtime( char *psz_buffer, mtime_t date ) { - static mtime_t ll1000 = 1000, ll60 = 60, ll24 = 24; + static const mtime_t ll1000 = 1000, ll60 = 60, ll24 = 24; snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02d:%02d:%02d-%03d.%03d", (int) (date / (ll1000 * ll1000 * ll60 * ll60) % ll24), @@ -97,202 +79,28 @@ char *mstrtime( char *psz_buffer, mtime_t date ) * \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. */ -char *secstotimestr( char *psz_buffer, int i_seconds ) +char *secstotimestr( char *psz_buffer, int32_t i_seconds ) { - snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%d:%2.2d:%2.2d", - (int) (i_seconds / (60 *60)), - (int) ((i_seconds / 60) % 60), - (int) (i_seconds % 60) ); - return( psz_buffer ); -} - - -/** - * Return high precision date - * - * Uses the gettimeofday() function when possible (1 MHz resolution) or the - * ftime() function (1 kHz resolution). - */ -mtime_t mdate( void ) -{ -#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 ) - return( real_time_clock_usecs() ); - -#elif defined( WIN32 ) || defined( UNDER_CE ) - /* We don't need the real date, just the value of a high precision timer */ - static mtime_t freq = I64C(-1); - - if( freq == I64C(-1) ) + if( unlikely(i_seconds < 0) ) { - /* 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. - */ - LARGE_INTEGER buf; - - freq = ( QueryPerformanceFrequency( &buf ) && - (freq == I64C(1193182) || freq == I64C(3579545) ) ) - ? buf.QuadPart : 0; + secstotimestr( psz_buffer + 1, -i_seconds ); + *psz_buffer = '-'; + return psz_buffer; } - if( freq != 0 ) - { - LARGE_INTEGER counter; - QueryPerformanceCounter (&counter); + div_t d; - /* 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); + d = div( i_seconds, 60 ); + i_seconds = d.rem; + d = div( d.quot, 60 ); - return (d.quot * 1000000) - + ((d.rem * 1000000) / freq); - } + if( d.quot ) + snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%u:%02u:%02u", + d.quot, d.rem, i_seconds ); 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; - mtime_t usec_time; - - if( i_wrap_counts == -1 ) - { - /* Initialization */ - i_previous_time = I64C(1000) * GetTickCount(); - 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; - } -#else - struct timeval tv_date; - - /* gettimeofday() cannot fail given &tv_date is a valid address */ - (void)gettimeofday( &tv_date, NULL ); - return( (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec ); -#endif -} - -/** - * Wait for a date - * - * 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. - * \param date The date to wake up at - */ -void mwait( mtime_t date ) -{ -#if defined (HAVE_CLOCK_NANOSLEEP) - lldiv_t d = lldiv( date, 1000000 ); - struct timespec ts = { d.quot, d.rem * 1000 }; - -# if (_POSIX_MONOTONIC_CLOCK - 0 >= 0) - if( clock_nanosleep( CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL ) ) -# endif - clock_nanosleep( CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL ); -#else - - mtime_t delay = date - mdate(); - if( delay > 0 ) - msleep( delay ); - -#endif -} - -/** - * More precise sleep() - * - * Portable usleep() function. - * \param delay the amount of time to sleep - */ -void msleep( mtime_t delay ) -{ -#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 ) - snooze( delay ); - -#elif defined( PTH_INIT_IN_PTH_H ) - pth_usleep( delay ); - -#elif defined( ST_INIT_IN_ST_H ) - st_usleep( delay ); - -#elif defined( WIN32 ) || defined( UNDER_CE ) - Sleep( (int) (delay / 1000) ); - -#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 ); - -#else - struct timeval tv_delay; - - tv_delay.tv_sec = delay / 1000000; - tv_delay.tv_usec = delay % 1000000; - - /* 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 - * can be ignored. */ - select( 0, NULL, NULL, NULL, &tv_delay ); -#endif + snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02u:%02u", + d.rem, i_seconds ); + return psz_buffer; } /* @@ -325,6 +133,8 @@ void date_Init( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d ) void date_Change( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d ) { + /* change time scale of remainder */ + p_date->i_remainder = p_date->i_remainder * i_divider_n / p_date->i_divider_num; p_date->i_divider_num = i_divider_n; p_date->i_divider_den = i_divider_d; } @@ -373,16 +183,82 @@ void date_Move( date_t *p_date, mtime_t i_difference ) */ 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); + mtime_t i_dividend = i_nb_samples * CLOCK_FREQ * p_date->i_divider_den; + lldiv_t d = lldiv( i_dividend, p_date->i_divider_num ); + + p_date->date += d.quot; + p_date->i_remainder += (int)d.rem; if( p_date->i_remainder >= p_date->i_divider_num ) { /* This is Bresenham algorithm. */ - p_date->date += p_date->i_divider_den; + assert( p_date->i_remainder < 2*p_date->i_divider_num); + p_date->date += 1; p_date->i_remainder -= p_date->i_divider_num; } return p_date->date; } + +/** + * Decrement the date and return the result, taking into account + * rounding errors. + * + * \param date to decrement + * \param decrementation in number of samples + * \return date value + */ +mtime_t date_Decrement( date_t *p_date, uint32_t i_nb_samples ) +{ + mtime_t i_dividend = (mtime_t)i_nb_samples * CLOCK_FREQ * p_date->i_divider_den; + p_date->date -= i_dividend / p_date->i_divider_num; + unsigned i_rem_adjust = i_dividend % p_date->i_divider_num; + + if( p_date->i_remainder < i_rem_adjust ) + { + /* This is Bresenham algorithm. */ + assert( p_date->i_remainder < p_date->i_divider_num); + p_date->date -= 1; + p_date->i_remainder += p_date->i_divider_num; + } + + p_date->i_remainder -= i_rem_adjust; + + return p_date->date; +} + +/** + * @return NTP 64-bits timestamp in host byte order. + */ +uint64_t NTPtime64 (void) +{ +#if (_POSIX_TIMERS > 0) + struct timespec ts; + + clock_gettime (CLOCK_REALTIME, &ts); +#else + struct timeval tv; + struct + { + uint32_t tv_sec; + uint32_t tv_nsec; + } ts; + + 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 |= ((UINT64_C(70) * 365 + 17) * 24 * 60 * 60 + ts.tv_sec) << 32; + return t; +} +