From 1b85206c85cc0a6265d36442c9bad1c82caeae12 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 23 Aug 2007 06:57:08 +0200 Subject: [PATCH 1/8] time.h for initial! review --- src/lib/time.h | 191 ++++++++++++++++++++++++++++++++++++ tests/10timefunctions.tests | 31 +++++- 2 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 src/lib/time.h diff --git a/src/lib/time.h b/src/lib/time.h new file mode 100644 index 000000000..421736860 --- /dev/null +++ b/src/lib/time.h @@ -0,0 +1,191 @@ +/* + time.h - Time and frame calculations + + Copyright (C) CinelerraCV + 2007, Christian Thaeter + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CINELERRA_TIME_H +#define CINELERRA_TIME_H + +#include +#include + +#include "error.h" + +/* + this time functions are small macro like wrapers, they are all inlined for performance reasons + time is passed around as pointers, this pointer must never be NULL + + timehandling is a delicate business, be careful of precision errors accumulating, TODO explain how to use time + +*/ + +/* over or underflow (tried to make a movie which has negative length? or more than some hundreds days?) */ +CINELERRA_ERROR_DECLARE(TIME_RANGE); + +/* + Note: we measure time starting from zero, + time never becomes negative + (I didnt checked if the time types are signed) +*/ +typedef struct timeval cinelerra_time; +typedef cinelerra_time* CinelerraTime; + +/** + * framerates are defined as a rational number and a base time (usually 1sec (do we need this?)) + * for example NTSC with 29.97fps is {2997, 100, {1, 0}} + */ +struct cinelerra_framerate_struct +{ + unsigned numerator; + unsigned denominator; + cinelerra_time base; +}; + +typedef struct cinelerra_framerate_struct cinelerra_framerate; +typedef cinelerra_framerate CinelerraFramerate; + +typedef unsigned long cinelerra_frame; + + + + +/** set a time value to zero. + */ +inline CinelerraTime +cinelerra_time_clear (CinelerraTime time) +{ + REQUIRE (time); + time->tv_sec = 0; + time->tv_usec = 0; + return time; +} + +/** get current time. + */ +inline CinelerraTime +cinelerra_time_current (CinelerraTime time) +{ + REQUIRE (time); + /* gettime should never ever fail in a correct program */ + if (gettimeofday (time, 0)) + CINELERRA_DIE; + + return time; +} + +/** init from floating point representation. + */ +inline CinelerraTime +cinelerra_time_set_double (CinelerraTime time, double fp) +{ + REQUIRE (time); + REQUIRE (fp >= 0.0); + + time->tv_sec = fp; + time->tv_usec = (fp - time->tv_sec) * 1000000.0; + return time; +} + +/** convert to floating point repesentation. + */ +inline double +cinelerra_time_double_get (CinelerraTime time) +{ + REQUIRE (time); + double fp; + + fp = time->tv_sec; + fp += time->tv_usec / 1000000.0; + return fp; +} + + + +/** convert from floating point repesentation. + */ +inline CinelerraTime +cinelerra_time_set_double (CinelerraTime time, double fp) +{ + REQUIRE (time); + REQUIRE (fp >= 0.0); + + time->tv_sec = fp; + time->tv_usec = (fp - time->tv_sec) * 1000000.0; + return time; +} + +/* internal function */ +inline void +cinelerra_time_normalize (CinelerraTime time) +{ + if (time->tv_usec >= 1000000) + { + time->tv_sec += (time->tv_usec / 1000000); + time->tv_usec = (time->tv_usec % 1000000); + } +} + +/** copy time + */ +inline CinelerraTime +cinelerra_time_copy (CinelerraTime dest, const CinelerraTime src) +{ + REQUIRE (dest); + REQUIRE (src); + dest->tv_sec = src->tv_sec; + dest->tv_usec = src->tv_usec; + return time; +} + +/** add time. + */ +inline CinelerraTime +cinelerra_time_add (CinelerraTime dest, const CinelerraTime to_add) +{ + REQUIRE (dest); + REQUIRE (to_add); + REQUIRE (dest->tv_sec + to_add->tv_sec > dest->tv_sec, "time overflow"); + TODO ("handling overflow as error?"); + + dest->tv_sec += to_add->tv_sec; + time->tv_usec += to_add->tv_usec; + + cinelerra_time_normalize (dest); + return dest; +} + +/** substact time. + */ +inline CinelerraTime +cinelerra_time_sub (CinelerraTime dest, const CinelerraTime to_sub) +{ + REQUIRE (dest); + REQUIRE (to_sub); + REQUIRE (dest->tv_sec - to_sub->tv_sec < dest->tv_sec, "time underflow"); + TODO ("handling underflow as error?"); + + dest->tv_sec -= to_sub->tv_sec; + time->tv_usec -= to_sub->tv_usec; + + cinelerra_time_normalize (dest); + return dest; +} + + +#endif diff --git a/tests/10timefunctions.tests b/tests/10timefunctions.tests index 764d91fd8..dd08aca5f 100644 --- a/tests/10timefunctions.tests +++ b/tests/10timefunctions.tests @@ -1,9 +1,32 @@ -TESTING "Time functions" +TESTING "Time functions" ./test_time -PLANNED "get time" -PLANNED "add time, normalized" -PLANNED "substract time, normalized" +TEST "time to float" tofloat 15 500000 < Date: Thu, 23 Aug 2007 11:15:01 +0200 Subject: [PATCH 2/8] added error handling, removed timebase from framerate --- src/lib/time.h | 162 +++++++++++++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 65 deletions(-) diff --git a/src/lib/time.h b/src/lib/time.h index 421736860..ef0979bd0 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -36,7 +36,9 @@ */ /* over or underflow (tried to make a movie which has negative length? or more than some hundreds days?) */ -CINELERRA_ERROR_DECLARE(TIME_RANGE); +CINELERRA_ERROR_DECLARE(TIME_OVERFLOW); +CINELERRA_ERROR_DECLARE(TIME_UNDERFLOW); +CINELERRA_ERROR_DECLARE(TIME_NEGATIVE); /* Note: we measure time starting from zero, @@ -47,93 +49,101 @@ typedef struct timeval cinelerra_time; typedef cinelerra_time* CinelerraTime; /** - * framerates are defined as a rational number and a base time (usually 1sec (do we need this?)) - * for example NTSC with 29.97fps is {2997, 100, {1, 0}} + * framerates are defined as a rational number + * for example NTSC with 29.97fps is {2997, 100} */ struct cinelerra_framerate_struct { unsigned numerator; unsigned denominator; - cinelerra_time base; }; typedef struct cinelerra_framerate_struct cinelerra_framerate; -typedef cinelerra_framerate CinelerraFramerate; +typedef cinelerra_framerate* CinelerraFramerate; typedef unsigned long cinelerra_frame; -/** set a time value to zero. +/** + * set a time value to zero. */ inline CinelerraTime cinelerra_time_clear (CinelerraTime time) { - REQUIRE (time); - time->tv_sec = 0; - time->tv_usec = 0; + if(time) + { + time->tv_sec = 0; + time->tv_usec = 0; + } return time; } -/** get current time. +/** + * get current time. */ inline CinelerraTime cinelerra_time_current (CinelerraTime time) { - REQUIRE (time); - /* gettime should never ever fail in a correct program */ - if (gettimeofday (time, 0)) - CINELERRA_DIE; - + if (time) + { + /* gettime should never ever fail in a correct program */ + if (gettimeofday (time, NULL)) + CINELERRA_DIE; + } return time; } -/** init from floating point representation. +/** + * init from floating point representation. */ inline CinelerraTime cinelerra_time_set_double (CinelerraTime time, double fp) { - REQUIRE (time); - REQUIRE (fp >= 0.0); - - time->tv_sec = fp; - time->tv_usec = (fp - time->tv_sec) * 1000000.0; - return time; + if (time) + { + if (fp >= 0.0) + { + time->tv_sec = fp; + time->tv_usec = (fp - time->tv_sec) * 1000000.0; + return time; + } + else + { + cinelerra_error_set(CINELERRA_ERROR_TIME_NEGATIVE); + } + } + return NULL; } -/** convert to floating point repesentation. +/** + * convert to floating point repesentation. */ inline double cinelerra_time_double_get (CinelerraTime time) { - REQUIRE (time); - double fp; + if (time) + { + double fp; - fp = time->tv_sec; - fp += time->tv_usec / 1000000.0; - return fp; + fp = time->tv_sec; + fp += time->tv_usec / 1000000.0; + return fp; + } + return NAN; } -/** convert from floating point repesentation. +/** + * normalize time after operations. + * used internally */ -inline CinelerraTime -cinelerra_time_set_double (CinelerraTime time, double fp) -{ - REQUIRE (time); - REQUIRE (fp >= 0.0); - - time->tv_sec = fp; - time->tv_usec = (fp - time->tv_sec) * 1000000.0; - return time; -} - -/* internal function */ inline void cinelerra_time_normalize (CinelerraTime time) { + REQUIRE (time); if (time->tv_usec >= 1000000) { time->tv_sec += (time->tv_usec / 1000000); @@ -141,49 +151,71 @@ cinelerra_time_normalize (CinelerraTime time) } } -/** copy time +/** + * copy time */ inline CinelerraTime cinelerra_time_copy (CinelerraTime dest, const CinelerraTime src) { - REQUIRE (dest); - REQUIRE (src); - dest->tv_sec = src->tv_sec; - dest->tv_usec = src->tv_usec; - return time; + if (dest && src) + { + dest->tv_sec = src->tv_sec; + dest->tv_usec = src->tv_usec; + } + return test; } -/** add time. +/** + * add time. */ inline CinelerraTime -cinelerra_time_add (CinelerraTime dest, const CinelerraTime to_add) +cinelerra_time_add (CinelerraTime dest, const CinelerraTime src) { - REQUIRE (dest); - REQUIRE (to_add); - REQUIRE (dest->tv_sec + to_add->tv_sec > dest->tv_sec, "time overflow"); - TODO ("handling overflow as error?"); + if (dest && src) + { + time_t t = dest->tv_sec; - dest->tv_sec += to_add->tv_sec; - time->tv_usec += to_add->tv_usec; + dest->tv_sec += src->tv_sec; + time->tv_usec += src->tv_usec; - cinelerra_time_normalize (dest); + cinelerra_time_normalize (dest); + + if (dest->tv_sec < t) + { + cinelerra_error_set (CINELERRA_ERROR_TIME_OVERFLOW); + return NULL; + } + } return dest; } -/** substact time. +/** + * substact time. */ inline CinelerraTime -cinelerra_time_sub (CinelerraTime dest, const CinelerraTime to_sub) +cinelerra_time_sub (CinelerraTime dest, const CinelerraTime src) { - REQUIRE (dest); - REQUIRE (to_sub); - REQUIRE (dest->tv_sec - to_sub->tv_sec < dest->tv_sec, "time underflow"); - TODO ("handling underflow as error?"); + if (dest && src) + { + time_t t = dest->tv_sec; - dest->tv_sec -= to_sub->tv_sec; - time->tv_usec -= to_sub->tv_usec; + dest->tv_sec -= src->tv_sec; + if (time->tv_usec >= src->tv_usec) + time->tv_usec -= src->tv_usec; + else + { + --dest->tv_sec; + time->tv_usec += src->tv_usec; + } - cinelerra_time_normalize (dest); + cinelerra_time_normalize (dest); + + if (dest->tv_sec > t) + { + cinelerra_error_set (CINELERRA_ERROR_TIME_UNDERFLOW); + return NULL; + } + } return dest; } From 2976b2ab29d2010df37502e43d72c42b5d2eb0c8 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 24 Aug 2007 06:02:29 +0200 Subject: [PATCH 3/8] time handling ok so far --- src/lib/Makefile.am | 13 +++++- src/lib/time.c | 26 ++++++++++++ src/lib/time.h | 100 +++++++++++++++++++++++++++++++------------- 3 files changed, 107 insertions(+), 32 deletions(-) create mode 100644 src/lib/time.c diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 7419bedf9..ed81d9a5b 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -19,6 +19,15 @@ libcin3_a_srcdir = $(top_srcdir)/src/lib noinst_LIBRARIES += libcin3.a libcin3_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Werror +libcin3_a_CPPFLAGS = -I$(top_srcdir)/src/ + +libcin3_a_SOURCES = \ + $(libcin3_a_srcdir)/plugin.c \ + $(libcin3_a_srcdir)/error.c \ + $(libcin3_a_srcdir)/time.c + +noinst_HEADERS += \ + $(libcin3_a_srcdir)/plugin.h \ + $(libcin3_a_srcdir)/error.h \ + $(libcin3_a_srcdir)/time.h -libcin3_a_SOURCES = $(libcin3_a_srcdir)/plugin.c $(libcin3_a_srcdir)/error.c -noinst_HEADERS += $(libcin3_a_srcdir)/plugin.h $(libcin3_a_srcdir)/error.h diff --git a/src/lib/time.c b/src/lib/time.c new file mode 100644 index 000000000..6cbdde6ee --- /dev/null +++ b/src/lib/time.c @@ -0,0 +1,26 @@ +/* + time.h - Time and frame calculations + + Copyright (C) CinelerraCV + 2007, Christian Thaeter + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "lib/error.h" + +CINELERRA_ERROR_DEFINE(TIME_OVERFLOW, "Time overflow"); +CINELERRA_ERROR_DEFINE(TIME_UNDERFLOW, "Time underflow"); +CINELERRA_ERROR_DEFINE(TIME_NEGATIVE, "Time negative"); diff --git a/src/lib/time.h b/src/lib/time.h index ef0979bd0..78f0854e1 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -24,8 +24,9 @@ #include #include +#include -#include "error.h" +#include "lib/error.h" /* this time functions are small macro like wrapers, they are all inlined for performance reasons @@ -64,12 +65,26 @@ typedef cinelerra_framerate* CinelerraFramerate; typedef unsigned long cinelerra_frame; +/** + * normalize time after operations. + * used internally + */ +static inline void +cinelerra_time_normalize (CinelerraTime time) +{ + REQUIRE (time); + if (time->tv_usec >= 1000000) + { + time->tv_sec += (time->tv_usec / 1000000); + time->tv_usec = (time->tv_usec % 1000000); + } +} /** * set a time value to zero. */ -inline CinelerraTime +static inline CinelerraTime cinelerra_time_clear (CinelerraTime time) { if(time) @@ -83,7 +98,7 @@ cinelerra_time_clear (CinelerraTime time) /** * get current time. */ -inline CinelerraTime +static inline CinelerraTime cinelerra_time_current (CinelerraTime time) { if (time) @@ -98,7 +113,7 @@ cinelerra_time_current (CinelerraTime time) /** * init from floating point representation. */ -inline CinelerraTime +static inline CinelerraTime cinelerra_time_set_double (CinelerraTime time, double fp) { if (time) @@ -106,21 +121,62 @@ cinelerra_time_set_double (CinelerraTime time, double fp) if (fp >= 0.0) { time->tv_sec = fp; - time->tv_usec = (fp - time->tv_sec) * 1000000.0; + time->tv_usec = round((fp - time->tv_sec) * 1000000.0); return time; } else { + time->tv_sec = (time_t)-1; + time->tv_usec = (suseconds_t)-1; cinelerra_error_set(CINELERRA_ERROR_TIME_NEGATIVE); } } return NULL; } +/** + * initialize with seconds and microseconds. + */ +static inline CinelerraTime +cinelerra_time_init (CinelerraTime time, time_t sec, suseconds_t usec) +{ + if (time) + { + time->tv_sec = sec; + time->tv_usec = usec; + cinelerra_time_normalize (time); + } + return time; +} + +/** + * get the seconds part from a time. + */ +static inline time_t +cinelerra_time_sec (CinelerraTime time) +{ + if (time) + return time->tv_sec; + else + return (time_t)-1; +} + +/** + * get the microseconds part of a time. + */ +static inline suseconds_t +cinelerra_time_usec (CinelerraTime time) +{ + if (time) + return time->tv_usec; + else + return (suseconds_t)-1; +} + /** * convert to floating point repesentation. */ -inline double +static inline double cinelerra_time_double_get (CinelerraTime time) { if (time) @@ -135,26 +191,10 @@ cinelerra_time_double_get (CinelerraTime time) } - -/** - * normalize time after operations. - * used internally - */ -inline void -cinelerra_time_normalize (CinelerraTime time) -{ - REQUIRE (time); - if (time->tv_usec >= 1000000) - { - time->tv_sec += (time->tv_usec / 1000000); - time->tv_usec = (time->tv_usec % 1000000); - } -} - /** * copy time */ -inline CinelerraTime +static inline CinelerraTime cinelerra_time_copy (CinelerraTime dest, const CinelerraTime src) { if (dest && src) @@ -162,13 +202,13 @@ cinelerra_time_copy (CinelerraTime dest, const CinelerraTime src) dest->tv_sec = src->tv_sec; dest->tv_usec = src->tv_usec; } - return test; + return dest; } /** * add time. */ -inline CinelerraTime +static inline CinelerraTime cinelerra_time_add (CinelerraTime dest, const CinelerraTime src) { if (dest && src) @@ -176,7 +216,7 @@ cinelerra_time_add (CinelerraTime dest, const CinelerraTime src) time_t t = dest->tv_sec; dest->tv_sec += src->tv_sec; - time->tv_usec += src->tv_usec; + dest->tv_usec += src->tv_usec; cinelerra_time_normalize (dest); @@ -192,7 +232,7 @@ cinelerra_time_add (CinelerraTime dest, const CinelerraTime src) /** * substact time. */ -inline CinelerraTime +static inline CinelerraTime cinelerra_time_sub (CinelerraTime dest, const CinelerraTime src) { if (dest && src) @@ -200,12 +240,12 @@ cinelerra_time_sub (CinelerraTime dest, const CinelerraTime src) time_t t = dest->tv_sec; dest->tv_sec -= src->tv_sec; - if (time->tv_usec >= src->tv_usec) - time->tv_usec -= src->tv_usec; + if (dest->tv_usec >= src->tv_usec) + dest->tv_usec -= src->tv_usec; else { --dest->tv_sec; - time->tv_usec += src->tv_usec; + dest->tv_usec += 1000000 - src->tv_usec; } cinelerra_time_normalize (dest); From 9472c115e76f026e5ebd8b8592b57b7d2b0ce1d7 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 24 Aug 2007 06:02:51 +0200 Subject: [PATCH 4/8] tests for time handling --- tests/10timefunctions.tests | 36 +++++++++---- tests/Makefile.am | 6 ++- tests/time/DIR_INFO | 1 + tests/time/test-time.c | 101 ++++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 tests/time/DIR_INFO create mode 100644 tests/time/test-time.c diff --git a/tests/10timefunctions.tests b/tests/10timefunctions.tests index dd08aca5f..e2582ddc6 100644 --- a/tests/10timefunctions.tests +++ b/tests/10timefunctions.tests @@ -1,28 +1,44 @@ -TESTING "Time functions" ./test_time +TESTING "Time functions" ./test-time -TEST "time to float" tofloat 15 500000 < + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include + +#include "lib/time.h" + + +CINELERRA_ERROR_DEFINE(TEST, "test error"); + +int +main (int argc, char** argv) +{ + if (argc == 1) + return 0; + + if (!strcmp(argv[1], "init")) + { + cinelerra_time time; + + cinelerra_time_init (&time, atol (argv[2]), atol(argv[3])); + + printf ("%lu %lu\n", (long)cinelerra_time_sec (&time), (long)cinelerra_time_usec (&time)); + } + + if (!strcmp(argv[1], "todouble")) + { + cinelerra_time time; + + cinelerra_time_init (&time, atol (argv[2]), atol(argv[3])); + + printf ("%g\n", cinelerra_time_double_get (&time)); + } + + if (!strcmp(argv[1], "todoublenull")) + { + printf ("%g\n", cinelerra_time_double_get (NULL)); + } + + if (!strcmp(argv[1], "fromdouble")) + { + cinelerra_time time; + + cinelerra_time_set_double (&time, atof (argv[2])); + + printf ("%lu %lu\n", (long)cinelerra_time_sec (&time), (long)cinelerra_time_usec (&time)); + } + + if (!strcmp(argv[1], "currenttime")) + { + cinelerra_time time; + + cinelerra_time_current (&time); + + printf ("%lu %lu\n", (long)cinelerra_time_sec (&time), (long)cinelerra_time_usec (&time)); + } + + if (!strcmp(argv[1], "add")) + { + cinelerra_time time1, time2; + + cinelerra_time_init (&time1, 0, atol (argv[2])); + cinelerra_time_init (&time2, 0, atol (argv[3])); + cinelerra_time_add (&time1, &time2); + + printf ("%lu %lu\n", (long)cinelerra_time_sec (&time1), (long)cinelerra_time_usec (&time1)); + } + + if (!strcmp(argv[1], "sub")) + { + cinelerra_time time1, time2; + + cinelerra_time_init (&time1, 0, atol (argv[2])); + cinelerra_time_init (&time2, 0, atol (argv[3])); + cinelerra_time_sub (&time1, &time2); + + printf ("%lu %lu\n", (long)cinelerra_time_sec (&time1), (long)cinelerra_time_usec (&time1)); + } + + + return 0; +} From c2898d26f2495487b791043279d1f096d134e3fc Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sat, 25 Aug 2007 03:46:07 +0200 Subject: [PATCH 5/8] put framerate caclulation in a single file --- src/lib/framerate.h | 79 +++++++++++++++++++++++++++++++++++++ src/lib/time.h | 18 +-------- tests/10timefunctions.tests | 72 +++++++++++++++++++++++++++++++-- tests/time/test-time.c | 23 +++++++++++ 4 files changed, 172 insertions(+), 20 deletions(-) create mode 100644 src/lib/framerate.h diff --git a/src/lib/framerate.h b/src/lib/framerate.h new file mode 100644 index 000000000..f7f075f91 --- /dev/null +++ b/src/lib/framerate.h @@ -0,0 +1,79 @@ +/* + framerate.h - framerate calculations + + Copyright (C) CinelerraCV + 2007, Christian Thaeter + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CINELERRA_FRAMERATE_H +#define CINELERRA_FRAMERATE_H + +#include "lib/error.h" +#include "lib/time.h" + +/** + * framerates are defined as a rational number + * for example NTSC with 29.97fps is {2997, 100} + */ +struct cinelerra_framerate_struct +{ + unsigned n; //numerator + unsigned d; //denominator +}; + +typedef struct cinelerra_framerate_struct cinelerra_framerate; +typedef cinelerra_framerate* CinelerraFramerate; + +typedef unsigned long cinelerra_framepos; + +/** + * Get the frame number of a given time at a given frame rate. + * frame indexing starts with 1 + */ +static inline cinelerra_framepos +cinelerra_framerate_frame_get_time (const CinelerraFramerate framerate, CinelerraTime time) +{ + REQUIRE (framerate); + TODO ("if(!time).."); + /* we add a magic microsecond for rounding, because of integer truncation frames may be calculated at most 1us earlier, + the idea is to compensate odd framerates which fall out off microsecond precision. + */ + return ((time->tv_sec * 1000000ULL + time->tv_usec + /*magic*/1) * framerate->n)/(framerate->d * 1000000ULL) + 1; +} + +/** + * Get the start time for a frame. + * frame indexing starts with 1 + */ +static inline CinelerraTime +cinelerra_framerate_time_get_time_frame (const CinelerraFramerate framerate, + CinelerraTime time, + cinelerra_framepos frame) +{ + REQUIRE (framerate); + REQUIRE (frame>0); + TODO ("if(!time).."); + + unsigned long long usec = (frame-1) * (framerate->d * 1000000ULL) / framerate->n; + + time->tv_sec = usec / 1000000; + time->tv_usec = usec % 1000000; + + return time; +} + +#endif diff --git a/src/lib/time.h b/src/lib/time.h index 78f0854e1..a2dea483b 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -1,5 +1,5 @@ /* - time.h - Time and frame calculations + time.h - Time calculations Copyright (C) CinelerraCV 2007, Christian Thaeter @@ -49,22 +49,6 @@ CINELERRA_ERROR_DECLARE(TIME_NEGATIVE); typedef struct timeval cinelerra_time; typedef cinelerra_time* CinelerraTime; -/** - * framerates are defined as a rational number - * for example NTSC with 29.97fps is {2997, 100} - */ -struct cinelerra_framerate_struct -{ - unsigned numerator; - unsigned denominator; -}; - -typedef struct cinelerra_framerate_struct cinelerra_framerate; -typedef cinelerra_framerate* CinelerraFramerate; - -typedef unsigned long cinelerra_frame; - - /** * normalize time after operations. * used internally diff --git a/tests/10timefunctions.tests b/tests/10timefunctions.tests index e2582ddc6..fe4603e0a 100644 --- a/tests/10timefunctions.tests +++ b/tests/10timefunctions.tests @@ -44,10 +44,76 @@ END PLANNED "overflow" PLANNED "underflow" +# frame 1 begins at time 0 +TEST "time to frame NTSC frame 1 begin" ntscframefromtime 0 0 < #include "lib/time.h" +#include "lib/frames.h" CINELERRA_ERROR_DEFINE(TEST, "test error"); @@ -30,6 +31,8 @@ CINELERRA_ERROR_DEFINE(TEST, "test error"); int main (int argc, char** argv) { + NOBUG_INIT; + if (argc == 1) return 0; @@ -96,6 +99,26 @@ main (int argc, char** argv) printf ("%lu %lu\n", (long)cinelerra_time_sec (&time1), (long)cinelerra_time_usec (&time1)); } + if (!strcmp(argv[1], "ntscframefromtime")) + { + cinelerra_framerate ntsc = {2997, 100}; + cinelerra_time time; + + cinelerra_time_init (&time, atol (argv[2]), atol (argv[3])); + + printf ("%lu\n", (long)cinelerra_framerate_frame_get_time (&ntsc, &time)); + } + + if (!strcmp(argv[1], "ntscframestart")) + { + cinelerra_framerate ntsc = {2997, 100}; + cinelerra_time time; + + cinelerra_framerate_time_get_time_frame (&ntsc, &time, atol (argv[2])); + + printf ("%lu %lu\n", (long)cinelerra_time_sec(&time), (long)cinelerra_time_usec(&time)); + } + return 0; } From e41059497f18f778727f71b41b48144583e01019 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 28 Aug 2007 19:28:41 +0200 Subject: [PATCH 6/8] refined and corrected framerate caclulation --- src/lib/Makefile.am | 3 ++- src/lib/framerate.h | 4 ++-- tests/10timefunctions.tests | 48 +++++++++++++++++++++---------------- tests/time/test-time.c | 28 +++++++++++++++++++--- 4 files changed, 56 insertions(+), 27 deletions(-) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index ed81d9a5b..56dff6cef 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -29,5 +29,6 @@ libcin3_a_SOURCES = \ noinst_HEADERS += \ $(libcin3_a_srcdir)/plugin.h \ $(libcin3_a_srcdir)/error.h \ - $(libcin3_a_srcdir)/time.h + $(libcin3_a_srcdir)/time.h \ + $(libcin3_a_srcdir)/framerate.h diff --git a/src/lib/framerate.h b/src/lib/framerate.h index f7f075f91..ef3561f87 100644 --- a/src/lib/framerate.h +++ b/src/lib/framerate.h @@ -27,7 +27,7 @@ /** * framerates are defined as a rational number - * for example NTSC with 29.97fps is {2997, 100} + * for example NTSC with 30000/1001fps */ struct cinelerra_framerate_struct { @@ -68,7 +68,7 @@ cinelerra_framerate_time_get_time_frame (const CinelerraFramerate framerate, REQUIRE (frame>0); TODO ("if(!time).."); - unsigned long long usec = (frame-1) * (framerate->d * 1000000ULL) / framerate->n; + unsigned long long usec = ((frame-1) * framerate->d * 1000000ULL - (/*magic*/frame>1?1:0)) / framerate->n; time->tv_sec = usec / 1000000; time->tv_usec = usec % 1000000; diff --git a/tests/10timefunctions.tests b/tests/10timefunctions.tests index fe4603e0a..835e1a918 100644 --- a/tests/10timefunctions.tests +++ b/tests/10timefunctions.tests @@ -69,16 +69,6 @@ TEST "time to frame NTSC frame 3 begin" ntscframefromtime 0 66733 <time->frame conversion 2" ntscframecheck 2 <time->frame conversion 3" ntscframecheck 3 <time->frame conversion 4" ntscframecheck 4 <time->frame conversion 5" ntscframecheck 5 <time->frame conversion 6" ntscframecheck 6 <time->frame conversion 7" ntscframecheck 7 <time->frame conversion 8" ntscframecheck 8 <time->frame conversion 9" ntscframecheck 9 <time->frame conversion 100000" ntscframecheck 100000 <time->frame conversion 999999" ntscframecheck 999999 < #include "lib/time.h" -#include "lib/frames.h" +#include "lib/framerate.h" CINELERRA_ERROR_DEFINE(TEST, "test error"); @@ -101,7 +101,7 @@ main (int argc, char** argv) if (!strcmp(argv[1], "ntscframefromtime")) { - cinelerra_framerate ntsc = {2997, 100}; + cinelerra_framerate ntsc = {30000, 1001}; cinelerra_time time; cinelerra_time_init (&time, atol (argv[2]), atol (argv[3])); @@ -111,7 +111,7 @@ main (int argc, char** argv) if (!strcmp(argv[1], "ntscframestart")) { - cinelerra_framerate ntsc = {2997, 100}; + cinelerra_framerate ntsc = {30000, 1001}; cinelerra_time time; cinelerra_framerate_time_get_time_frame (&ntsc, &time, atol (argv[2])); @@ -119,6 +119,28 @@ main (int argc, char** argv) printf ("%lu %lu\n", (long)cinelerra_time_sec(&time), (long)cinelerra_time_usec(&time)); } + if (!strcmp(argv[1], "ntscframecheck")) + { + cinelerra_framerate ntsc = {30000, 1001}; + cinelerra_time time1; + cinelerra_time time2; + cinelerra_framepos frame; + + cinelerra_framepos frame1; + cinelerra_framepos frame2; + + frame = atol (argv[2]); + + + cinelerra_framerate_time_get_time_frame (&ntsc, &time1, frame); + printf("frame %lu ", frame1 = cinelerra_framerate_frame_get_time (&ntsc, &time1)); + + cinelerra_time_init (&time2, 0, 1); + cinelerra_time_sub (&time1, &time2); + printf("%lu\n", frame2 = cinelerra_framerate_frame_get_time (&ntsc, &time1)); + + ENSURE (frame1 == frame2+1); + } return 0; } From 017e19c10810198321cc33314cdaac59f8ad5794 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 28 Aug 2007 22:24:46 +0200 Subject: [PATCH 7/8] small notes about time and frames --- wiki/support_library.html | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/wiki/support_library.html b/wiki/support_library.html index 6e8b5a717..144de95db 100644 --- a/wiki/support_library.html +++ b/wiki/support_library.html @@ -720,6 +720,19 @@ We use a common base class for all our application specific exceptions. These ex * getting standardized error messages automatically +
+
! Framerates
+Framerates are stored as rational numbers eg. 30000/1001 for NTSC, this representation allows highly precise integer calculations for frames and times.
+
+! Frames
+All calculations in cinelerra are based on frames which is a signed integer type. Together with a Framerate and a starting point, every frame can be exactly located.
+
+! Time
+Time is used only internally in cinelerra, every external representation of time will be converted to frames. Time has a precision of 1 microsecond and is stored in POSIX struct timeval. Time is always handled as absolute time, thus frame addresses map to absolute times, there is at worst a 1us precision jitter but no drift.
+
+!! SMPTE and other Timecodes
+will be added on demand, but be frame based, not time based
+
/***
 |Name|FullScreenPlugin|
@@ -2360,7 +2373,7 @@ h1,h2,h3,h4,h5,h6 {
 /*}}}*/
 
-
+
The Support Library contains all tools we need at various places, but by themselves don't defines a subsystem on their own.
 
 These things are:
@@ -2369,7 +2382,7 @@ These things are:
 * a wrapper for POSIX Threads
 ** Thread creation joining and canceling
 ** Locking primitives like Condition variables and Mutexes
-
+* [[Frame and Time handling and calculations|FrameAndTime]]
  
 (... to be continued)
 
From e438ad6e8fa1fcaa1277e4f6a0313d465a590883 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 29 Aug 2007 17:41:10 +0200 Subject: [PATCH 8/8] add documentation and error handling to framerate functions --- src/lib/Makefile.am | 5 +++-- src/lib/framerate.c | 25 +++++++++++++++++++++++++ src/lib/framerate.h | 40 ++++++++++++++++++++++++++++++++-------- tests/time/test-time.c | 19 ++++++++++--------- 4 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 src/lib/framerate.c diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 56dff6cef..91327cdf5 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -24,8 +24,9 @@ libcin3_a_CPPFLAGS = -I$(top_srcdir)/src/ libcin3_a_SOURCES = \ $(libcin3_a_srcdir)/plugin.c \ $(libcin3_a_srcdir)/error.c \ - $(libcin3_a_srcdir)/time.c - + $(libcin3_a_srcdir)/time.c \ + $(libcin3_a_srcdir)/framerate.c + noinst_HEADERS += \ $(libcin3_a_srcdir)/plugin.h \ $(libcin3_a_srcdir)/error.h \ diff --git a/src/lib/framerate.c b/src/lib/framerate.c new file mode 100644 index 000000000..138a338a3 --- /dev/null +++ b/src/lib/framerate.c @@ -0,0 +1,25 @@ +/* + framerate.c - framerate calculations + + Copyright (C) CinelerraCV + 2007, Christian Thaeter + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "lib/error.h" + +CINELERRA_ERROR_DEFINE(FRAMERATE_ILLEGAL_TIME, "invalid time given"); +CINELERRA_ERROR_DEFINE(FRAMERATE_ILLEGAL_FRAME, "invalid frame given"); diff --git a/src/lib/framerate.h b/src/lib/framerate.h index ef3561f87..2df5971fb 100644 --- a/src/lib/framerate.h +++ b/src/lib/framerate.h @@ -22,6 +22,8 @@ #ifndef CINELERRA_FRAMERATE_H #define CINELERRA_FRAMERATE_H +#include + #include "lib/error.h" #include "lib/time.h" @@ -38,17 +40,30 @@ struct cinelerra_framerate_struct typedef struct cinelerra_framerate_struct cinelerra_framerate; typedef cinelerra_framerate* CinelerraFramerate; -typedef unsigned long cinelerra_framepos; +typedef signed long cinelerra_framepos; + +CINELERRA_ERROR_DECLARE(FRAMERATE_ILLEGAL_TIME); +CINELERRA_ERROR_DECLARE(FRAMERATE_ILLEGAL_FRAME); + +#define CINELERRA_FRAMEPOS_ERROR LONG_MIN /** * Get the frame number of a given time at a given frame rate. * frame indexing starts with 1 + * @param framerate is a pointer to the framerate used, defined as rational number. Must be given. + * @param time is a pointer to a cinelerra_time which shall be converted. + * @return frame at the given time or CINELERRA_FRAMEPOS_ERROR on error. */ static inline cinelerra_framepos cinelerra_framerate_frame_get_time (const CinelerraFramerate framerate, CinelerraTime time) { REQUIRE (framerate); - TODO ("if(!time).."); + if (!time || time->tv_sec == (time_t)-1) + { + cinelerra_error_set(CINELERRA_ERROR_FRAMERATE_ILLEGAL_TIME); + return CINELERRA_FRAMEPOS_ERROR; + } + /* we add a magic microsecond for rounding, because of integer truncation frames may be calculated at most 1us earlier, the idea is to compensate odd framerates which fall out off microsecond precision. */ @@ -58,6 +73,10 @@ cinelerra_framerate_frame_get_time (const CinelerraFramerate framerate, Cinelerr /** * Get the start time for a frame. * frame indexing starts with 1 + * @param framerate is a pointer to the framerate used, defined as rational number. Must be given. + * @param time is a pointer to a cinelerra_time which shall take the result. + * @param frame frame number to be converted to time. This frame number must be greater than 0. + * @return the pointer given in time or NULL on error (or when it was given as time). */ static inline CinelerraTime cinelerra_framerate_time_get_time_frame (const CinelerraFramerate framerate, @@ -65,14 +84,19 @@ cinelerra_framerate_time_get_time_frame (const CinelerraFramerate framerate, cinelerra_framepos frame) { REQUIRE (framerate); - REQUIRE (frame>0); - TODO ("if(!time).."); + if (time) + { + if (frame < 1) + { + cinelerra_error_set(CINELERRA_ERROR_FRAMERATE_ILLEGAL_FRAME); + return NULL; + } - unsigned long long usec = ((frame-1) * framerate->d * 1000000ULL - (/*magic*/frame>1?1:0)) / framerate->n; - - time->tv_sec = usec / 1000000; - time->tv_usec = usec % 1000000; + unsigned long long usec = ((frame-1) * framerate->d * 1000000ULL - (/*magic*/frame>1?1:0)) / framerate->n; + time->tv_sec = usec / 1000000; + time->tv_usec = usec % 1000000; + } return time; } diff --git a/tests/time/test-time.c b/tests/time/test-time.c index 6bbc84833..519a65781 100644 --- a/tests/time/test-time.c +++ b/tests/time/test-time.c @@ -114,9 +114,8 @@ main (int argc, char** argv) cinelerra_framerate ntsc = {30000, 1001}; cinelerra_time time; - cinelerra_framerate_time_get_time_frame (&ntsc, &time, atol (argv[2])); - - printf ("%lu %lu\n", (long)cinelerra_time_sec(&time), (long)cinelerra_time_usec(&time)); + if(cinelerra_framerate_time_get_time_frame (&ntsc, &time, atol (argv[2]))) + printf ("%lu %lu\n", (long)cinelerra_time_sec(&time), (long)cinelerra_time_usec(&time)); } if (!strcmp(argv[1], "ntscframecheck")) @@ -132,14 +131,16 @@ main (int argc, char** argv) frame = atol (argv[2]); - cinelerra_framerate_time_get_time_frame (&ntsc, &time1, frame); - printf("frame %lu ", frame1 = cinelerra_framerate_frame_get_time (&ntsc, &time1)); + if (cinelerra_framerate_time_get_time_frame (&ntsc, &time1, frame)) + { + printf("frame %lu ", frame1 = cinelerra_framerate_frame_get_time (&ntsc, &time1)); - cinelerra_time_init (&time2, 0, 1); - cinelerra_time_sub (&time1, &time2); - printf("%lu\n", frame2 = cinelerra_framerate_frame_get_time (&ntsc, &time1)); + cinelerra_time_init (&time2, 0, 1); + cinelerra_time_sub (&time1, &time2); + printf("%lu\n", frame2 = cinelerra_framerate_frame_get_time (&ntsc, &time1)); + ENSURE (frame1 == frame2+1); + } - ENSURE (frame1 == frame2+1); } return 0;