Add support for NTSC drop-frame timecode.
This commit is contained in:
parent
94f8379aa2
commit
6a44134833
4 changed files with 189 additions and 4 deletions
|
|
@ -88,6 +88,24 @@ lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gavl_time_t
|
||||||
|
lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)
|
||||||
|
{
|
||||||
|
REQUIRE (mins < 60);
|
||||||
|
REQUIRE (secs < 60);
|
||||||
|
REQUIRE (frames < 30);
|
||||||
|
REQUIRE_IF (secs == 0 && mins % 10, frames >= 2, "non-existent frame in NTSC drop-frame");
|
||||||
|
|
||||||
|
int total_mins = 60 * hours + mins;
|
||||||
|
int total_frames = 108000 * hours
|
||||||
|
+ 1800 * mins
|
||||||
|
+ 30 * secs
|
||||||
|
+ frames
|
||||||
|
- 2 * (total_mins - total_mins / 10);
|
||||||
|
|
||||||
|
return (total_frames / 29.97f) * 1000 * GAVL_TIME_SCALE_MS;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
lumiera_time_hours (gavl_time_t time)
|
lumiera_time_hours (gavl_time_t time)
|
||||||
{
|
{
|
||||||
|
|
@ -125,3 +143,44 @@ lumiera_time_frame_count (gavl_time_t time, float fps)
|
||||||
|
|
||||||
return roundf((time / GAVL_TIME_SCALE_MS / 1000.0f) * fps);
|
return roundf((time / GAVL_TIME_SCALE_MS / 1000.0f) * fps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used in the NTSC drop-frame functions to avoid code
|
||||||
|
* repetition.
|
||||||
|
* @return the frame number for given time
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ntsc_drop_get_frame_number (gavl_time_t time)
|
||||||
|
{
|
||||||
|
int frame = lumiera_time_frame_count (time, NTSC_DROP_FRAME_FPS);
|
||||||
|
|
||||||
|
int d = frame / 17982; // 17982 = 600 * 29.97
|
||||||
|
int m = frame % 17982; // 17982 = 600 * 29.97
|
||||||
|
|
||||||
|
return frame + 18*d + 2*((m - 2) / 1798); // 1798 = 60 * 29.97
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
lumiera_time_ntsc_drop_frames (gavl_time_t time)
|
||||||
|
{
|
||||||
|
return ntsc_drop_get_frame_number(time) % 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
lumiera_time_ntsc_drop_seconds (gavl_time_t time)
|
||||||
|
{
|
||||||
|
return ntsc_drop_get_frame_number(time) / 30 % 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
lumiera_time_ntsc_drop_minutes (gavl_time_t time)
|
||||||
|
{
|
||||||
|
return ntsc_drop_get_frame_number(time) / 30 / 60 % 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
lumiera_time_ntsc_drop_hours (gavl_time_t time)
|
||||||
|
{
|
||||||
|
return ntsc_drop_get_frame_number(time) / 30 / 60 / 60 % 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <gavl/gavl.h>
|
#include <gavl/gavl.h>
|
||||||
|
|
||||||
|
#define NTSC_DROP_FRAME_FPS 29.97
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a time in a safeclib tmpbuf in HH:MM:SS:mmm format.
|
* Formats a time in a safeclib tmpbuf in HH:MM:SS:mmm format.
|
||||||
* @param size maximal length for the string
|
* @param size maximal length for the string
|
||||||
|
|
@ -55,6 +57,18 @@ lumiera_build_time (long millis, uint secs, uint mins, uint hours);
|
||||||
gavl_time_t
|
gavl_time_t
|
||||||
lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours);
|
lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a time value by summing up the given components.
|
||||||
|
* The components are interpreted as a NTSC drop-frame timecode.
|
||||||
|
* @param frames number of frames
|
||||||
|
* @param secs number of seconds
|
||||||
|
* @param mins number of minutes
|
||||||
|
* @param hours number of hours
|
||||||
|
* You must take care not to specify time codes that are illegal NTSC drop-frame times.
|
||||||
|
*/
|
||||||
|
gavl_time_t
|
||||||
|
lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the hour part of given time.
|
* Get the hour part of given time.
|
||||||
* @param time the time value to use
|
* @param time the time value to use
|
||||||
|
|
@ -99,4 +113,32 @@ lumiera_time_frames (gavl_time_t time, float fps);
|
||||||
int
|
int
|
||||||
lumiera_time_frame_count (gavl_time_t time, float fps);
|
lumiera_time_frame_count (gavl_time_t time, float fps);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the frame part of given time, using NTSC drop-frame timecode.
|
||||||
|
* @param time the time value to use
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lumiera_time_ntsc_drop_frames (gavl_time_t time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the second part of given time, using NTSC drop-frame timecode.
|
||||||
|
* @param time the time value to use
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lumiera_time_ntsc_drop_seconds (gavl_time_t time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the minute part of given time, using NTSC drop-frame timecode.
|
||||||
|
* @param time the time value to use
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lumiera_time_ntsc_drop_minutes (gavl_time_t time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hour part of given time, using NTSC drop-frame timecode.
|
||||||
|
* @param time the time value to use
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lumiera_time_ntsc_drop_hours (gavl_time_t time);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,6 @@ END
|
||||||
|
|
||||||
TEST "frame rate dependent calculations" fps <<END
|
TEST "frame rate dependent calculations" fps <<END
|
||||||
END
|
END
|
||||||
|
|
||||||
|
TEST "ntsc drop-frame calculations" ntsc_drop_frame << END
|
||||||
|
END
|
||||||
|
|
|
||||||
|
|
@ -89,4 +89,85 @@ TEST (fps)
|
||||||
CHECK (lumiera_time_frame_count (t, FPS+5) == 409498);
|
CHECK (lumiera_time_frame_count (t, FPS+5) == 409498);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 3. NTSC drop-frame calculations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
TEST (ntsc_drop_frame)
|
||||||
|
{
|
||||||
|
// Make sure frame 0 begins at 0
|
||||||
|
gavl_time_t t = lumiera_build_time_ntsc_drop (0, 0, 0, 0);
|
||||||
|
|
||||||
|
CHECK ((gavl_time_t) t == 0);
|
||||||
|
CHECK (lumiera_time_millis (t) == 0);
|
||||||
|
CHECK (lumiera_time_seconds (t) == 0);
|
||||||
|
CHECK (lumiera_time_minutes (t) == 0);
|
||||||
|
CHECK (lumiera_time_hours (t) == 0);
|
||||||
|
CHECK (lumiera_time_frames (t, FPS) == 0);
|
||||||
|
CHECK (lumiera_time_frames (t, FPS+5) == 0);
|
||||||
|
CHECK (lumiera_time_frame_count (t, FPS) == 0);
|
||||||
|
CHECK (lumiera_time_frame_count (t, FPS+5) == 0);
|
||||||
|
|
||||||
|
// Use some arbitrary value to test with
|
||||||
|
const int FRAMES = 15;
|
||||||
|
const int SECONDS = 20;
|
||||||
|
const int MINUTES = 55;
|
||||||
|
const int HOURS = 3;
|
||||||
|
const float FPS = 24.0;
|
||||||
|
|
||||||
|
t = lumiera_build_time_ntsc_drop (FRAMES, SECONDS, MINUTES, HOURS);
|
||||||
|
|
||||||
|
CHECK (lumiera_time_millis (t) == 487);
|
||||||
|
CHECK (lumiera_time_seconds (t) == SECONDS);
|
||||||
|
CHECK (lumiera_time_minutes (t) == MINUTES);
|
||||||
|
CHECK (lumiera_time_hours (t) == HOURS);
|
||||||
|
|
||||||
|
// Check standard frame calculations to verify build_time_ntsc_drop
|
||||||
|
CHECK (lumiera_time_frames (t, FPS) == 11);
|
||||||
|
CHECK (lumiera_time_frames (t, FPS+5) == 14);
|
||||||
|
CHECK (lumiera_time_frame_count (t, FPS) == 338892);
|
||||||
|
CHECK (lumiera_time_frame_count (t, FPS+5) == 409494);
|
||||||
|
|
||||||
|
// Frames for NTSC drop
|
||||||
|
CHECK (lumiera_time_ntsc_drop_frames (t) == 15);
|
||||||
|
CHECK (lumiera_time_frame_count (t, NTSC_DROP_FRAME_FPS) == 423191);
|
||||||
|
|
||||||
|
// Make sure we get back the same times. These tests are perhaps overly exhaustive.
|
||||||
|
int min;
|
||||||
|
int sec;
|
||||||
|
int frame;
|
||||||
|
int hrs;
|
||||||
|
for (hrs = 0; hrs <= 24; hrs += 6)
|
||||||
|
for (min = 0; min <= 59; min += 8)
|
||||||
|
for (sec = 0; sec <= 59; sec += 8)
|
||||||
|
for (frame = 0; frame <= 29; frame++)
|
||||||
|
{
|
||||||
|
// Skip dropped frames
|
||||||
|
if (min % 10 && sec == 0 && frame < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
t = lumiera_build_time_ntsc_drop(frame, sec, min, hrs);
|
||||||
|
|
||||||
|
/* ECHO ("%02d:%02d:%02d.%02d", */
|
||||||
|
/* lumiera_time_ntsc_drop_hours (t), */
|
||||||
|
/* lumiera_time_ntsc_drop_minutes (t), */
|
||||||
|
/* lumiera_time_ntsc_drop_seconds (t), */
|
||||||
|
/* lumiera_time_ntsc_drop_frames (t)); */
|
||||||
|
|
||||||
|
CHECK (lumiera_time_ntsc_drop_frames (t) == frame);
|
||||||
|
CHECK (lumiera_time_ntsc_drop_seconds (t) == sec);
|
||||||
|
CHECK (lumiera_time_ntsc_drop_minutes (t) == min);
|
||||||
|
CHECK (lumiera_time_ntsc_drop_hours (t) == hrs % 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we do not get non-existent frames
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 59; i++)
|
||||||
|
{
|
||||||
|
int frame = (i % 10 == 0) ? 0 : 2;
|
||||||
|
t = lumiera_build_time_ntsc_drop (frame, 0, i, 0);
|
||||||
|
CHECK (lumiera_time_ntsc_drop_frames (t) == frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TESTS_END
|
TESTS_END
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue