Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

mtime.c

00001 /*****************************************************************************
00002  * mtime.c: high resolution time management functions
00003  * Functions are prototyped in mtime.h.
00004  *****************************************************************************
00005  * Copyright (C) 1998-2004 the VideoLAN team
00006  * $Id: mtime.c 11664 2005-07-09 06:17:09Z courmisch $
00007  *
00008  * Authors: Vincent Seguin <[email protected]>
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00023  *****************************************************************************/
00024 
00025 /*
00026  * TODO:
00027  *  see if using Linux real-time extensions is possible and profitable
00028  */
00029 
00030 /*****************************************************************************
00031  * Preamble
00032  *****************************************************************************/
00033 #include <stdio.h>                                              /* sprintf() */
00034 
00035 #include <vlc/vlc.h>
00036 
00037 #if defined( PTH_INIT_IN_PTH_H )                                  /* GNU Pth */
00038 #   include <pth.h>
00039 #endif
00040 
00041 #ifdef HAVE_UNISTD_H
00042 #   include <unistd.h>                                           /* select() */
00043 #endif
00044 
00045 #ifdef HAVE_KERNEL_OS_H
00046 #   include <kernel/OS.h>
00047 #endif
00048 
00049 #if defined( WIN32 ) || defined( UNDER_CE )
00050 #   include <windows.h>
00051 #else
00052 #   include <sys/time.h>
00053 #endif
00054 
00055 #if defined(HAVE_NANOSLEEP) && !defined(HAVE_STRUCT_TIMESPEC)
00056 struct timespec
00057 {
00058     time_t  tv_sec;
00059     int32_t tv_nsec;
00060 };
00061 #endif
00062 
00063 #if defined(HAVE_NANOSLEEP) && !defined(HAVE_DECL_NANOSLEEP)
00064 int nanosleep(struct timespec *, struct timespec *);
00065 #endif
00066 
00077 char *mstrtime( char *psz_buffer, mtime_t date )
00078 {
00079     static mtime_t ll1000 = 1000, ll60 = 60, ll24 = 24;
00080 
00081     snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02d:%02d:%02d-%03d.%03d",
00082              (int) (date / (ll1000 * ll1000 * ll60 * ll60) % ll24),
00083              (int) (date / (ll1000 * ll1000 * ll60) % ll60),
00084              (int) (date / (ll1000 * ll1000) % ll60),
00085              (int) (date / ll1000 % ll1000),
00086              (int) (date % ll1000) );
00087     return( psz_buffer );
00088 }
00089 
00100 char *secstotimestr( char *psz_buffer, int i_seconds )
00101 {
00102     snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%d:%2.2d:%2.2d",
00103               (int) (i_seconds / (60 *60)),
00104               (int) ((i_seconds / 60) % 60),
00105               (int) (i_seconds % 60) );
00106     return( psz_buffer );
00107 }
00108 
00115 mtime_t mdate( void )
00116 {
00117 #if defined( HAVE_KERNEL_OS_H )
00118     return( real_time_clock_usecs() );
00119 
00120 #elif defined( WIN32 ) || defined( UNDER_CE )
00121     /* We don't need the real date, just the value of a high precision timer */
00122     static mtime_t freq = I64C(-1);
00123     mtime_t usec_time;
00124 
00125     if( freq == I64C(-1) )
00126     {
00127         /* Extract from the Tcl source code:
00128          * (http://www.cs.man.ac.uk/fellowsd-bin/TIP/7.html)
00129          *
00130          * Some hardware abstraction layers use the CPU clock
00131          * in place of the real-time clock as a performance counter
00132          * reference.  This results in:
00133          *    - inconsistent results among the processors on
00134          *      multi-processor systems.
00135          *    - unpredictable changes in performance counter frequency
00136          *      on "gearshift" processors such as Transmeta and
00137          *      SpeedStep.
00138          * There seems to be no way to test whether the performance
00139          * counter is reliable, but a useful heuristic is that
00140          * if its frequency is 1.193182 MHz or 3.579545 MHz, it's
00141          * derived from a colorburst crystal and is therefore
00142          * the RTC rather than the TSC.  If it's anything else, we
00143          * presume that the performance counter is unreliable.
00144          */
00145 
00146         freq = ( QueryPerformanceFrequency( (LARGE_INTEGER *)&freq ) &&
00147                  (freq == I64C(1193182) || freq == I64C(3579545) ) )
00148                ? freq : 0;
00149     }
00150 
00151     if( freq != 0 )
00152     {
00153         /* Microsecond resolution */
00154         QueryPerformanceCounter( (LARGE_INTEGER *)&usec_time );
00155         return ( usec_time * 1000000 ) / freq;
00156     }
00157     else
00158     {
00159         /* Fallback on GetTickCount() which has a milisecond resolution
00160          * (actually, best case is about 10 ms resolution)
00161          * GetTickCount() only returns a DWORD thus will wrap after
00162          * about 49.7 days so we try to detect the wrapping. */
00163 
00164         static CRITICAL_SECTION date_lock;
00165         static mtime_t i_previous_time = I64C(-1);
00166         static int i_wrap_counts = -1;
00167 
00168         if( i_wrap_counts == -1 )
00169         {
00170             /* Initialization */
00171             i_previous_time = I64C(1000) * GetTickCount();
00172             InitializeCriticalSection( &date_lock );
00173             i_wrap_counts = 0;
00174         }
00175 
00176         EnterCriticalSection( &date_lock );
00177         usec_time = I64C(1000) *
00178             (i_wrap_counts * I64C(0x100000000) + GetTickCount());
00179         if( i_previous_time > usec_time )
00180         {
00181             /* Counter wrapped */
00182             i_wrap_counts++;
00183             usec_time += I64C(0x100000000000);
00184         }
00185         i_previous_time = usec_time;
00186         LeaveCriticalSection( &date_lock );
00187 
00188         return usec_time;
00189     }
00190 
00191 #else
00192     struct timeval tv_date;
00193 
00194     /* gettimeofday() could return an error, and should be tested. However, the
00195      * only possible error, according to 'man', is EFAULT, which can not happen
00196      * here, since tv is a local variable. */
00197     gettimeofday( &tv_date, NULL );
00198     return( (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec );
00199 
00200 #endif
00201 }
00202 
00211 void mwait( mtime_t date )
00212 {
00213 #if defined( HAVE_KERNEL_OS_H )
00214     mtime_t delay;
00215 
00216     delay = date - real_time_clock_usecs();
00217     if( delay <= 0 )
00218     {
00219         return;
00220     }
00221     snooze( delay );
00222 
00223 #elif defined( WIN32 ) || defined( UNDER_CE )
00224     mtime_t usec_time, delay;
00225 
00226     usec_time = mdate();
00227     delay = date - usec_time;
00228     if( delay <= 0 )
00229     {
00230         return;
00231     }
00232     msleep( delay );
00233 
00234 #else
00235 
00236     struct timeval tv_date;
00237     mtime_t        delay;          /* delay in msec, signed to detect errors */
00238 
00239     /* see mdate() about gettimeofday() possible errors */
00240     gettimeofday( &tv_date, NULL );
00241 
00242     /* calculate delay and check if current date is before wished date */
00243     delay = date - (mtime_t) tv_date.tv_sec * 1000000
00244                  - (mtime_t) tv_date.tv_usec
00245                  - 10000;
00246 
00247     /* Linux/i386 has a granularity of 10 ms. It's better to be in advance
00248      * than to be late. */
00249     if( delay <= 0 )                 /* wished date is now or already passed */
00250     {
00251         return;
00252     }
00253 
00254 #   if defined( PTH_INIT_IN_PTH_H )
00255     pth_usleep( delay );
00256 
00257 #   elif defined( ST_INIT_IN_ST_H )
00258     st_usleep( delay );
00259 
00260 #   else
00261 
00262 #       if defined( HAVE_NANOSLEEP )
00263     {
00264         struct timespec ts_delay;
00265         ts_delay.tv_sec = delay / 1000000;
00266         ts_delay.tv_nsec = (delay % 1000000) * 1000;
00267 
00268         nanosleep( &ts_delay, NULL );
00269     }
00270 
00271 #       else
00272     tv_date.tv_sec = delay / 1000000;
00273     tv_date.tv_usec = delay % 1000000;
00274     /* see msleep() about select() errors */
00275     select( 0, NULL, NULL, NULL, &tv_date );
00276 #       endif
00277 
00278 #   endif
00279 
00280 #endif
00281 }
00282 
00289 void msleep( mtime_t delay )
00290 {
00291 #if defined( HAVE_KERNEL_OS_H )
00292     snooze( delay );
00293 
00294 #elif defined( PTH_INIT_IN_PTH_H )
00295     pth_usleep( delay );
00296 
00297 #elif defined( ST_INIT_IN_ST_H )
00298     st_usleep( delay );
00299 
00300 #elif defined( WIN32 ) || defined( UNDER_CE )
00301     Sleep( (int) (delay / 1000) );
00302 
00303 #elif defined( HAVE_NANOSLEEP )
00304     struct timespec ts_delay;
00305 
00306     ts_delay.tv_sec = delay / 1000000;
00307     ts_delay.tv_nsec = (delay % 1000000) * 1000;
00308 
00309     nanosleep( &ts_delay, NULL );
00310 
00311 #else
00312     struct timeval tv_delay;
00313 
00314     tv_delay.tv_sec = delay / 1000000;
00315     tv_delay.tv_usec = delay % 1000000;
00316 
00317     /* select() return value should be tested, since several possible errors
00318      * can occur. However, they should only happen in very particular occasions
00319      * (i.e. when a signal is sent to the thread, or when memory is full), and
00320      * can be ignored. */
00321     select( 0, NULL, NULL, NULL, &tv_delay );
00322 
00323 #endif
00324 }
00325 
00326 /*
00327  * Date management (internal and external)
00328  */
00329 
00338 void date_Init( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d )
00339 {
00340     p_date->date = 0;
00341     p_date->i_divider_num = i_divider_n;
00342     p_date->i_divider_den = i_divider_d;
00343     p_date->i_remainder = 0;
00344 }
00345 
00354 void date_Change( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d )
00355 {
00356     p_date->i_divider_num = i_divider_n;
00357     p_date->i_divider_den = i_divider_d;
00358 }
00359 
00366 void date_Set( date_t *p_date, mtime_t i_new_date )
00367 {
00368     p_date->date = i_new_date;
00369     p_date->i_remainder = 0;
00370 }
00371 
00378 mtime_t date_Get( const date_t *p_date )
00379 {
00380     return p_date->date;
00381 }
00382 
00389 void date_Move( date_t *p_date, mtime_t i_difference )
00390 {
00391     p_date->date += i_difference;
00392 }
00393 
00402 mtime_t date_Increment( date_t *p_date, uint32_t i_nb_samples )
00403 {
00404     mtime_t i_dividend = (mtime_t)i_nb_samples * 1000000;
00405     p_date->date += i_dividend / p_date->i_divider_num * p_date->i_divider_den;
00406     p_date->i_remainder += (int)(i_dividend % p_date->i_divider_num);
00407 
00408     if( p_date->i_remainder >= p_date->i_divider_num )
00409     {
00410         /* This is Bresenham algorithm. */
00411         p_date->date += p_date->i_divider_den;
00412         p_date->i_remainder -= p_date->i_divider_num;
00413     }
00414 
00415     return p_date->date;
00416 }

Generated on Tue Dec 20 10:15:00 2005 for vlc-0.8.4a by  doxygen 1.4.2