Skip to content

Commit

Permalink
Videos: Avoid float math for frame durations
Browse files Browse the repository at this point in the history
SMK format actually defines frame durations in units that are 0.01ms:

https://github.com/diasurgical/libsmackerdec/blob/91e732bb6953489077430572f43fc802bf2c75b2/src/SmackerDecoder.cpp#L329-L334

That's great because it means we can do everything using integer math.
  • Loading branch information
glebm authored and AJenbo committed Oct 3, 2024
1 parent 1078598 commit f476754
Showing 1 changed file with 29 additions and 8 deletions.
37 changes: 29 additions & 8 deletions Source/storm/storm_svid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,33 @@ std::uint8_t SVidAudioDepth;
std::unique_ptr<int16_t[]> SVidAudioBuffer;
#endif

// Smacker's atomic time unit is a one hundred thousand's of a second (i.e. 0.01 millisecond, or 10 microseconds).
// We use SDL ticks for timing, which have millisecond resolution.
// There are 100 Smacker time units in a millisecond.
constexpr uint64_t SmackerTimeUnit = 100;
constexpr uint64_t TimeMsToSmk(uint64_t ms) { return ms * SmackerTimeUnit; }
constexpr uint64_t TimeSmkToMs(uint64_t time) { return time / SmackerTimeUnit; };
uint64_t GetTicksSmk()
{
#if SDL_VERSION_ATLEAST(2, 0, 18)
return TimeMsToSmk(SDL_GetTicks64());
#else
return TimeMsToSmk(SDL_GetTicks());
#endif
}

uint32_t SVidWidth, SVidHeight;
double SVidFrameEnd;
double SVidFrameLength;
bool SVidLoop;
SmackerHandle SVidHandle;
std::unique_ptr<uint8_t[]> SVidFrameBuffer;
SDLPaletteUniquePtr SVidPalette;
SDLSurfaceUniquePtr SVidSurface;

// The end of the current frame (time in SMK time units from the start of the program).
uint64_t SVidFrameEnd;
// The length of a frame in SMK time units.
uint32_t SVidFrameLength;

bool IsLandscapeFit(unsigned long srcW, unsigned long srcH, unsigned long dstW, unsigned long dstH)
{
return srcW * dstH > dstW * srcH;
Expand Down Expand Up @@ -278,7 +296,10 @@ bool SVidPlayBegin(const char *filename, int flags)
}
#endif

SVidFrameLength = 1000000.0 / Smacker_GetFrameRate(SVidHandle);
// SMK format internally defines the frame rate as the frame duration
// in either milliseconds or SMK time units (0.01ms). The library converts it
// to FPS, which is always an integer, and here we convert it back to SMK time units.
SVidFrameLength = 100000 / static_cast<uint32_t>(Smacker_GetFrameRate(SVidHandle));
Smacker_GetFrameSize(SVidHandle, SVidWidth, SVidHeight);

#ifndef USE_SDL1
Expand Down Expand Up @@ -318,7 +339,7 @@ bool SVidPlayBegin(const char *filename, int flags)
SVidPalette = SDLWrap::AllocPalette();
UpdatePalette();

SVidFrameEnd = SDL_GetTicks() * 1000.0 + SVidFrameLength;
SVidFrameEnd = GetTicksSmk() + SVidFrameLength;

return true;
}
Expand All @@ -329,7 +350,7 @@ bool SVidPlayContinue()
UpdatePalette();
}

if (SDL_GetTicks() * 1000.0 >= SVidFrameEnd) {
if (GetTicksSmk() >= SVidFrameEnd) {
return SVidLoadNextFrame(); // Skip video and audio if the system is to slow
}

Expand All @@ -345,16 +366,16 @@ bool SVidPlayContinue()
}
#endif

if (SDL_GetTicks() * 1000.0 >= SVidFrameEnd) {
if (GetTicksSmk() >= SVidFrameEnd) {
return SVidLoadNextFrame(); // Skip video if the system is to slow
}

if (!BlitFrame())
return false;

double now = SDL_GetTicks() * 1000.0;
uint64_t now = GetTicksSmk();
if (now < SVidFrameEnd) {
SDL_Delay(static_cast<Uint32>((SVidFrameEnd - now) / 1000.0)); // wait with next frame if the system is too fast
SDL_Delay(static_cast<Uint32>(TimeSmkToMs(SVidFrameEnd - now))); // wait with next frame if the system is too fast
}

return SVidLoadNextFrame();
Expand Down

0 comments on commit f476754

Please sign in to comment.