Tuesday, August 12, 2008

Playback framework, high resolution timers.

I've advanced in the playback framework - the core of our editor. I've designed the AVController class, and I'm in the playback part - the part where you move data from the input to the output and keep the video and audio in sync.

For that I had to program a high-resolution (millisecond precision) function. Basing myself on the SDL's API, I build the syGetTicks() function - it gives you the number of milliseconds that have passed since the program was started. Unfortunately the stupid Windows API didn't have a Unix time-compatible function, so I had to break my head trying to make it work.

The Windows GetSystemTimeAsFiletime returns a 64-bit integer (well, 2 32-bit integers actually) that gives you the number of 100-nanosecond units (Whiskey Tango Foxtrot?!) since 1600. Wha? How am I supposed to convert that?

Well, easy. You just divide it by 10,000,000. And how to do that with 32 bit math?

Easy. Let's use some algebra.

(A * 2^32 + B ) / C = (B/C) + (A*2^32 / C )

The low part of the division is taken care of. And the second, is just as simple:

(A * 2^32) / 10^7 = A * (2^32/10^7) = A * 429.4967296

What do you think? We only have to multiply the high part by a floating point number and we'll get our result. However... we don't want to use floating point math in a high-resolution timing routine. So Instead, we'll do this:

A * 429.4967296 = (A*429) + (A*0.496) + (A*0.0007296)

Luckily, these numbers have fractional equivalents.

A * 429.4967296 = (A*429) + ((A*62)/125) + ((A*57)/78125).

Ta-da! So the final result is:

result = (low / 10000000) + ((hi*57)/78125) + ((hi*62)/125) + (hi* 429);

And we have the WINNT 32-bit equivalent for obtaining the number of seconds since... something.

If at startup we obtain an initial counter, for subsequent calls we only need to substract that number and we'll obtain the number of seconds that have ellapsed since we turned on our PC.

To obtain the milliseconds for the ticks, it was easier. Windows has a GetTickCount() function, but we can't use it since it based itself on the number of milliseconds since midnight. So we just obtain the modulo 1000 and stay with the milliseconds part.

Here are the final functions. The sy prefix is for "saya". Note that the Windows part hasn't been tested yet :P
If you want the final version, please check the Saya-VE source code (SVN) at
http://developer.berlios.de/projects/saya/


/**************************************************************
* Cross-platform High resolution timer functions.
* Copyright: Ricardo Garcia
* Website: http://sayavideoeditor.sourceforge.net/
* License: WxWindows License
**************************************************************/
unsigned long syGetTime();

unsigned long sySecondsAtInit = syGetTime();

unsigned long syGetTime() {
unsigned long result;
#ifdef __WIN32__
FILETIME ft;
GetSystemTimeAsFiletime(&ft);
unsigned long low = ft.dwLowDateTime;
/* We spare the highest 16 bits;
we don't want to overflow the calculation. */
unsigned long hi = ft.dwHighDateTime & 0x0ffff;
result = (low / 10000000) +
((hi*57)/78125) +
((hi*62)/125) +
(hi* 429);
#else
struct timeval mytime;
gettimeofday(&mytime, NULL);
result = (unsigned long)(mytime.tv_sec);
#endif
return result;
}

unsigned long syGetTicks() {
unsigned long result;
#ifdef __WIN32__
result = (syGetTime() - sySecondsAtInit) +
(GetTickCount() % 1000);
#else
struct timeval mytime;
gettimeofday(&mytime, NULL);
result = (unsigned long)(mytime.tv_sec - sySecondsAtInit)*1000;
result += (((unsigned long)(mytime.tv_usec)) / 1000);
#endif
return result;
}

2 comments:

rick_777 said...

result = (low / 10000000) + ((hi*57)/78125) + ((hi*62)/125) + (hi* 429);

A calculation with uses less bits (and leaves more room before overflow) is:

result = (low / 10000000) + (hi >> 1) - ((hi << 1)/625) - (hi/20000) - ((hi << 1)/100000) - (hi/2500000)

but I really don't know if it's worth the change. We use twice the divisions.

rick_777 said...

Duh. Turns out the Windows GetTickCount() doesn't count from midnight but since the machine was started. Bye bye algorithm :P