Merge branch 'library'

This commit is contained in:
Christian Thaeter 2007-08-30 00:33:38 +02:00
commit 98d3716fcb
10 changed files with 700 additions and 12 deletions

View file

@ -19,6 +19,17 @@ 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 \
$(libcin3_a_srcdir)/framerate.c
noinst_HEADERS += \
$(libcin3_a_srcdir)/plugin.h \
$(libcin3_a_srcdir)/error.h \
$(libcin3_a_srcdir)/time.h \
$(libcin3_a_srcdir)/framerate.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

25
src/lib/framerate.c Normal file
View file

@ -0,0 +1,25 @@
/*
framerate.c - framerate calculations
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
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");

103
src/lib/framerate.h Normal file
View file

@ -0,0 +1,103 @@
/*
framerate.h - framerate calculations
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
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 <limits.h>
#include "lib/error.h"
#include "lib/time.h"
/**
* framerates are defined as a rational number
* for example NTSC with 30000/1001fps
*/
struct cinelerra_framerate_struct
{
unsigned n; //numerator
unsigned d; //denominator
};
typedef struct cinelerra_framerate_struct cinelerra_framerate;
typedef cinelerra_framerate* CinelerraFramerate;
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);
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.
*/
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
* @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,
CinelerraTime time,
cinelerra_framepos frame)
{
REQUIRE (framerate);
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;
}
return time;
}
#endif

26
src/lib/time.c Normal file
View file

@ -0,0 +1,26 @@
/*
time.h - Time and frame calculations
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
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");

247
src/lib/time.h Normal file
View file

@ -0,0 +1,247 @@
/*
time.h - Time calculations
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
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 <sys/time.h>
#include <time.h>
#include <math.h>
#include "lib/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_OVERFLOW);
CINELERRA_ERROR_DECLARE(TIME_UNDERFLOW);
CINELERRA_ERROR_DECLARE(TIME_NEGATIVE);
/*
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;
/**
* 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.
*/
static inline CinelerraTime
cinelerra_time_clear (CinelerraTime time)
{
if(time)
{
time->tv_sec = 0;
time->tv_usec = 0;
}
return time;
}
/**
* get current time.
*/
static inline CinelerraTime
cinelerra_time_current (CinelerraTime time)
{
if (time)
{
/* gettime should never ever fail in a correct program */
if (gettimeofday (time, NULL))
CINELERRA_DIE;
}
return time;
}
/**
* init from floating point representation.
*/
static inline CinelerraTime
cinelerra_time_set_double (CinelerraTime time, double fp)
{
if (time)
{
if (fp >= 0.0)
{
time->tv_sec = fp;
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.
*/
static inline double
cinelerra_time_double_get (CinelerraTime time)
{
if (time)
{
double fp;
fp = time->tv_sec;
fp += time->tv_usec / 1000000.0;
return fp;
}
return NAN;
}
/**
* copy time
*/
static inline CinelerraTime
cinelerra_time_copy (CinelerraTime dest, const CinelerraTime src)
{
if (dest && src)
{
dest->tv_sec = src->tv_sec;
dest->tv_usec = src->tv_usec;
}
return dest;
}
/**
* add time.
*/
static inline CinelerraTime
cinelerra_time_add (CinelerraTime dest, const CinelerraTime src)
{
if (dest && src)
{
time_t t = dest->tv_sec;
dest->tv_sec += src->tv_sec;
dest->tv_usec += src->tv_usec;
cinelerra_time_normalize (dest);
if (dest->tv_sec < t)
{
cinelerra_error_set (CINELERRA_ERROR_TIME_OVERFLOW);
return NULL;
}
}
return dest;
}
/**
* substact time.
*/
static inline CinelerraTime
cinelerra_time_sub (CinelerraTime dest, const CinelerraTime src)
{
if (dest && src)
{
time_t t = dest->tv_sec;
dest->tv_sec -= src->tv_sec;
if (dest->tv_usec >= src->tv_usec)
dest->tv_usec -= src->tv_usec;
else
{
--dest->tv_sec;
dest->tv_usec += 1000000 - src->tv_usec;
}
cinelerra_time_normalize (dest);
if (dest->tv_sec > t)
{
cinelerra_error_set (CINELERRA_ERROR_TIME_UNDERFLOW);
return NULL;
}
}
return dest;
}
#endif

View file

@ -1,15 +1,126 @@
TESTING "Time functions"
TESTING "Time functions" ./test-time
PLANNED "get time"
PLANNED "add time, normalized"
PLANNED "substract time, normalized"
TEST "time init" init 15 500000 <<END
out: 15 500000
END
TEST "time normalize" init 0 1500000 <<END
out: 1 500000
END
TEST "time to float" todouble 15 500000 <<END
out: 15.5
END
TEST "time to float, NULL gives a NaN" todoublenull <<END
out: nan
END
TEST "float to time" fromdouble 33.6666666661 <<END
out: 33 666667
END
TEST "float to time, round down" fromdouble 0.00000049 <<END
out: 0 0
END
TEST "float to time, round up" fromdouble 0.0000005 <<END
out: 0 1
END
TEST "current time" currenttime <<END
return: 0
END
TEST "add time, normalized" add 1000001 2000002 <<END
out: 3 3
END
TEST "substract time, normalized" sub 3000003 2000002 <<END
out: 1 1
END
PLANNED "overflow"
PLANNED "underflow"
# frame 1 begins at time 0
TEST "time to frame NTSC frame 1 begin" ntscframefromtime 0 0 <<END
out: 1
END
# and ends at 33365
TEST "time to frame NTSC frame 1 end" ntscframefromtime 0 33365 <<END
out: 1
END
# thus 33366 is start of frame 2
TEST "time to frame NTSC frame 2 begin" ntscframefromtime 0 33366 <<END
out: 2
END
# and 66732 is the end of frame 2
TEST "time to frame NTSC frame 2 end" ntscframefromtime 0 66732 <<END
out: 2
END
# this makes 66733 the start of frame 3
TEST "time to frame NTSC frame 3 begin" ntscframefromtime 0 66733 <<END
out: 3
END
# after 1 hour we are at frame 107893
TEST "time to frame NTSC after 1hour" ntscframefromtime 3600 0 <<END
out: 107893
END
# after 200 days we are at frame 517881601
TEST "time to frame NTSC after 200 days" ntscframefromtime 17280000 0 <<END
out: 517882118
END
# the next ones for different common framerates, check corner cases
PLANNED "time to frame"
PLANNED "frame to time"
TEST "frame to time NTSC frame 1" ntscframestart 1 <<END
out: 0 0
END
TEST "frame to time NTSC frame 2" ntscframestart 2 <<END
out: 0 33366
END
TEST "frame->time->frame conversion 2" ntscframecheck 2 <<END
out: frame 2 1
END
TEST "frame->time->frame conversion 3" ntscframecheck 3 <<END
out: frame 3 2
END
TEST "frame->time->frame conversion 4" ntscframecheck 4 <<END
out: frame 4 3
END
TEST "frame->time->frame conversion 5" ntscframecheck 5 <<END
out: frame 5 4
END
TEST "frame->time->frame conversion 6" ntscframecheck 6 <<END
out: frame 6 5
END
TEST "frame->time->frame conversion 7" ntscframecheck 7 <<END
out: frame 7 6
END
TEST "frame->time->frame conversion 8" ntscframecheck 8 <<END
out: frame 8 7
END
TEST "frame->time->frame conversion 9" ntscframecheck 9 <<END
out: frame 9 8
END
TEST "frame->time->frame conversion 100000" ntscframecheck 100000 <<END
out: frame 100000 99999
END
TEST "frame->time->frame conversion 999999" ntscframecheck 999999 <<END
out: frame 999999 999998
END
# how long are N frames on average (beware of precision!)
PLANNED "frame duration"

View file

@ -18,10 +18,14 @@
tests_srcdir = $(top_srcdir)/tests
check_PROGRAMS += test-error
check_PROGRAMS += test-error test-time
test_error_SOURCES = $(tests_srcdir)/error/errortest.c
test_error_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
test_error_LDADD = $(builddir)/libcin3.a -lnobugmt -lpthread -ldl
test_time_SOURCES = $(tests_srcdir)/time/test-time.c
test_time_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
test_time_LDADD = $(builddir)/libcin3.a -lnobugmt -lpthread -ldl -lm
TESTS = $(tests_srcdir)/test.sh

1
tests/time/DIR_INFO Normal file
View file

@ -0,0 +1 @@
test time and frame calculations

147
tests/time/test-time.c Normal file
View file

@ -0,0 +1,147 @@
/*
test-time.c - test time and framerate functions
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
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 <stdio.h>
#include <string.h>
#include "lib/time.h"
#include "lib/framerate.h"
CINELERRA_ERROR_DEFINE(TEST, "test error");
int
main (int argc, char** argv)
{
NOBUG_INIT;
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));
}
if (!strcmp(argv[1], "ntscframefromtime"))
{
cinelerra_framerate ntsc = {30000, 1001};
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 = {30000, 1001};
cinelerra_time 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"))
{
cinelerra_framerate ntsc = {30000, 1001};
cinelerra_time time1;
cinelerra_time time2;
cinelerra_framepos frame;
cinelerra_framepos frame1;
cinelerra_framepos frame2;
frame = atol (argv[2]);
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));
ENSURE (frame1 == frame2+1);
}
}
return 0;
}

View file

@ -720,6 +720,19 @@ We use a common base class for all our application specific exceptions. These ex
* getting standardized error messages automatically
</pre>
</div>
<div title="FrameAndTime" modifier="CehTeh" modified="200708282024" created="200708261938" changecount="2">
<pre>! 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</pre>
</div>
<div title="FullScreenPlugin" modifier="CehTeh" modified="200706110313" created="200607241016" tags="systemConfig lewcidExtension" server.type="file" server.host="file:///home/ct/.homepage/home.html" server.page.revision="200706110313">
<pre>/***
|Name|FullScreenPlugin|
@ -2360,7 +2373,7 @@ h1,h2,h3,h4,h5,h6 {
/*}}}*/
</pre>
</div>
<div title="SupportLibrary" modifier="MichaelPloujnikov" modified="200707160346" created="200707102314" changecount="6">
<div title="SupportLibrary" modifier="CehTeh" modified="200708261935" created="200707102314" changecount="8">
<pre>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)
</pre>