From d2702e82540e747aa72118d355b1c4ee24339a64 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Fri, 10 Dec 2010 12:53:39 +0100 Subject: [PATCH 1/4] Add frame counting capabilities to time conversion lib. --- src/lib/time.c | 13 +++++++++++++ src/lib/time.h | 14 ++++++++++++++ tests/library/test-time.c | 9 +++++++++ 3 files changed, 36 insertions(+) diff --git a/src/lib/time.c b/src/lib/time.c index 5af40d640..3748d7e8d 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -91,3 +91,16 @@ lumiera_time_millis(gavl_time_t time) { return (time / GAVL_TIME_SCALE_MS) % 1000; } + +int +lumiera_time_frames(gavl_time_t time, float fps) +{ + return (fps * (lumiera_time_millis(time))) / 1000; +} + +int +lumiera_time_frame_count(gavl_time_t time, float fps) +{ + int ms = (time / GAVL_TIME_SCALE_MS); + return fps * ms / 1000; +} diff --git a/src/lib/time.h b/src/lib/time.h index f812c1b9d..4a3812238 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -60,5 +60,19 @@ int lumiera_time_seconds(gavl_time_t time); */ int lumiera_time_millis(gavl_time_t time); +/** + * Get the frame part of given time, using the given number of frames. + * @param gavl_time_t the time we are interested in converting + * @param fps Frame rate (float for now, but should be a rational) + */ +int lumiera_time_frames(gavl_time_t time, float fps); + +/** + * Get the frame count for the given time. + * @param gavl_time_t the time we are interested in converting + * @param fps Frame rate (float for now, but should be a rational) + */ +int lumiera_time_frame_count(gavl_time_t time, float fps); + #endif diff --git a/tests/library/test-time.c b/tests/library/test-time.c index 4ce8b63bb..fc0f4f343 100644 --- a/tests/library/test-time.c +++ b/tests/library/test-time.c @@ -32,6 +32,7 @@ const int MILLIS = 700; const int SECONDS = 20; const int MINUTES = 55; const int HOURS = 3; +const float FPS = 24.0; /* * 1. Basic functionality @@ -46,6 +47,10 @@ TEST (basic) { 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); ECHO ("%s", lumiera_tmpbuf_print_time(t)); @@ -56,6 +61,10 @@ TEST (basic) { CHECK (lumiera_time_seconds(t) == SECONDS); CHECK (lumiera_time_minutes(t) == MINUTES); CHECK (lumiera_time_hours(t) == HOURS); + CHECK (lumiera_time_frames(t, FPS) == (int)((FPS * MILLIS) / 1000)); + CHECK (lumiera_time_frames(t, FPS+5) == (int)(((FPS+5) * MILLIS) / 1000)); + CHECK (lumiera_time_frame_count(t, FPS) == 338896); + CHECK (lumiera_time_frame_count(t, FPS+5) == 409500); ECHO ("%s", lumiera_tmpbuf_print_time(t)); } From 94f8379aa2c4984969a3fe5343c40fd1a1e9b042 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Mon, 13 Dec 2010 18:16:15 +0100 Subject: [PATCH 2/4] Improved frame counting capabilities for time lib. Unit tests. --- src/lib/time.c | 39 ++++++++++++++++++------ src/lib/time.h | 52 ++++++++++++++++++++++--------- tests/15time.tests | 3 ++ tests/library/test-time.c | 64 +++++++++++++++++++++++++-------------- 4 files changed, 113 insertions(+), 45 deletions(-) diff --git a/src/lib/time.c b/src/lib/time.c index 3748d7e8d..337456db3 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -22,6 +22,7 @@ #include #include "lib/time.h" #include "lib/tmpbuf.h" +#include /* GAVL_TIME_SCALE is the correct factor or dividend when using gavl_time_t for * units of whole seconds from gavl_time_t. Since we want to use milliseconds, @@ -58,8 +59,12 @@ lumiera_tmpbuf_print_time (gavl_time_t time) } gavl_time_t -lumiera_build_time(long millis, uint secs, uint mins, uint hours) +lumiera_build_time (long millis, uint secs, uint mins, uint hours) { + REQUIRE (millis >= 0 && millis <= 999); + REQUIRE (mins < 60); + REQUIRE (secs < 60); + gavl_time_t time = millis + 1000 * secs + 1000 * 60 * mins @@ -68,39 +73,55 @@ lumiera_build_time(long millis, uint secs, uint mins, uint hours) return time; } +gavl_time_t +lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours) +{ + REQUIRE (mins < 60); + REQUIRE (secs < 60); + REQUIRE (frames < fps); + + gavl_time_t time = frames * (1000.0 / fps) + + 1000 * secs + + 1000 * 60 * mins + + 1000 * 60 * 60 * hours; + time *= GAVL_TIME_SCALE_MS; + return time; +} + int -lumiera_time_hours(gavl_time_t time) +lumiera_time_hours (gavl_time_t time) { return time / GAVL_TIME_SCALE_MS / 1000 / 60 / 60; } int -lumiera_time_minutes(gavl_time_t time) +lumiera_time_minutes (gavl_time_t time) { return (time / GAVL_TIME_SCALE_MS / 1000 / 60) % 60; } int -lumiera_time_seconds(gavl_time_t time) +lumiera_time_seconds (gavl_time_t time) { return (time / GAVL_TIME_SCALE_MS / 1000) % 60; } int -lumiera_time_millis(gavl_time_t time) +lumiera_time_millis (gavl_time_t time) { return (time / GAVL_TIME_SCALE_MS) % 1000; } int -lumiera_time_frames(gavl_time_t time, float fps) +lumiera_time_frames (gavl_time_t time, float fps) { return (fps * (lumiera_time_millis(time))) / 1000; } int -lumiera_time_frame_count(gavl_time_t time, float fps) +lumiera_time_frame_count (gavl_time_t time, float fps) { - int ms = (time / GAVL_TIME_SCALE_MS); - return fps * ms / 1000; + REQUIRE (fps > 0); + + return roundf((time / GAVL_TIME_SCALE_MS / 1000.0f) * fps); } diff --git a/src/lib/time.h b/src/lib/time.h index 4a3812238..df7b1dfb0 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -36,43 +36,67 @@ lumiera_tmpbuf_print_time (gavl_time_t time); /** * Builds a time value by summing up the given components. + * @param millis number of milliseconds + * @param secs number of seconds + * @param mins number of minutes + * @param hours number of hours */ gavl_time_t lumiera_build_time (long millis, uint secs, uint mins, uint hours); /** - * Get the hour part of given time. + * Builds a time value by summing up the given components. + * @param fps the frame rate to be used + * @param frames number of frames + * @param secs number of seconds + * @param mins number of minutes + * @param hours number of hours */ -int lumiera_time_hours(gavl_time_t time); +gavl_time_t +lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours); + +/** + * Get the hour part of given time. + * @param time the time value to use + */ +int +lumiera_time_hours (gavl_time_t time); /** * Get the minute part of given time. + * @param time the time value to use */ -int lumiera_time_minutes(gavl_time_t time); +int +lumiera_time_minutes (gavl_time_t time); /** * Get the seconds part of given time. + * @param time the time value to use */ -int lumiera_time_seconds(gavl_time_t time); +int +lumiera_time_seconds (gavl_time_t time); /** * Get the milliseconds part of given time. + * @param time the time value to use */ -int lumiera_time_millis(gavl_time_t time); +int +lumiera_time_millis (gavl_time_t time); /** - * Get the frame part of given time, using the given number of frames. - * @param gavl_time_t the time we are interested in converting - * @param fps Frame rate (float for now, but should be a rational) + * Get the frame part of given time, using the given fps. + * @param time the time value to use + * @param fps frame rate (float for now, but should be a rational) */ -int lumiera_time_frames(gavl_time_t time, float fps); +int +lumiera_time_frames (gavl_time_t time, float fps); /** - * Get the frame count for the given time. - * @param gavl_time_t the time we are interested in converting - * @param fps Frame rate (float for now, but should be a rational) + * Get the frame count for the given time, using the given fps. + * @param time the time value to use + * @param fps frame rate (float for now, but should be a rational) */ -int lumiera_time_frame_count(gavl_time_t time, float fps); +int +lumiera_time_frame_count (gavl_time_t time, float fps); #endif - diff --git a/tests/15time.tests b/tests/15time.tests index 0cb57ddcc..30ae5515c 100644 --- a/tests/15time.tests +++ b/tests/15time.tests @@ -4,3 +4,6 @@ TEST "basic functionality" basic < Date: Mon, 13 Dec 2010 18:22:12 +0100 Subject: [PATCH 3/4] Add support for NTSC drop-frame timecode. --- src/lib/time.c | 67 ++++++++++++++++++++++++++++++-- src/lib/time.h | 42 ++++++++++++++++++++ tests/15time.tests | 3 ++ tests/library/test-time.c | 81 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 4 deletions(-) diff --git a/src/lib/time.c b/src/lib/time.c index 337456db3..43ed80d34 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -34,14 +34,14 @@ lumiera_tmpbuf_print_time (gavl_time_t time) { int milliseconds, seconds, minutes, hours; int negative; - + if(time < 0) { negative = 1; time = -time; } else negative = 0; - + time /= GAVL_TIME_SCALE_MS; milliseconds = time % 1000; time /= 1000; @@ -50,10 +50,10 @@ lumiera_tmpbuf_print_time (gavl_time_t time) minutes = time % 60; time /= 60; hours = time; - + char *buffer = lumiera_tmpbuf_snprintf(64, "%s%02d:%02d:%02d.%03d", negative ? "-" : "", hours, minutes, seconds, milliseconds); - + ENSURE(buffer != NULL); return buffer; } @@ -88,6 +88,24 @@ lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours 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 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); } + +/** + * 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; +} + diff --git a/src/lib/time.h b/src/lib/time.h index df7b1dfb0..2b3e9523d 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -25,6 +25,8 @@ #include #include +#define NTSC_DROP_FRAME_FPS 29.97 + /** * Formats a time in a safeclib tmpbuf in HH:MM:SS:mmm format. * @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 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. * @param time the time value to use @@ -99,4 +113,32 @@ lumiera_time_frames (gavl_time_t time, float fps); int 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 diff --git a/tests/15time.tests b/tests/15time.tests index 30ae5515c..2dcedbea9 100644 --- a/tests/15time.tests +++ b/tests/15time.tests @@ -7,3 +7,6 @@ END TEST "frame rate dependent calculations" fps < Date: Tue, 14 Dec 2010 14:22:33 +0100 Subject: [PATCH 4/4] Remove preconditions requiring correct formatting of input times in time lib. For example, one could not specify minutes > 59 nor milliseconds > 999. Other parts of the code however relied on the fact that one could give arbitrary times in all fields (hours, milliseconds and minutes). --- src/lib/time.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/lib/time.c b/src/lib/time.c index 43ed80d34..6e486262e 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -61,10 +61,6 @@ lumiera_tmpbuf_print_time (gavl_time_t time) gavl_time_t lumiera_build_time (long millis, uint secs, uint mins, uint hours) { - REQUIRE (millis >= 0 && millis <= 999); - REQUIRE (mins < 60); - REQUIRE (secs < 60); - gavl_time_t time = millis + 1000 * secs + 1000 * 60 * mins @@ -76,10 +72,6 @@ lumiera_build_time (long millis, uint secs, uint mins, uint hours) gavl_time_t lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours) { - REQUIRE (mins < 60); - REQUIRE (secs < 60); - REQUIRE (frames < fps); - gavl_time_t time = frames * (1000.0 / fps) + 1000 * secs + 1000 * 60 * mins @@ -91,11 +83,6 @@ lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours 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