diff --git a/admin/scons/Platform.py b/admin/scons/Platform.py index ce2c4a5b3..d26e36d34 100644 --- a/admin/scons/Platform.py +++ b/admin/scons/Platform.py @@ -85,7 +85,7 @@ def configure(env): if not conf.CheckPkgConfig('gavl', '1.4'): - problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].') + problems.append('Did not find Gmerlin Audio Video Lib [https://github.com/bplaum/gavl].') else: conf.env.mergeConf('gavl') diff --git a/doc/devel/rfc/TimeHandling.txt b/doc/devel/rfc/TimeHandling.txt index 6896bc966..360f75c83 100644 --- a/doc/devel/rfc/TimeHandling.txt +++ b/doc/devel/rfc/TimeHandling.txt @@ -20,7 +20,7 @@ Description * We use an uniform time type. Time is time, not frames, samples etc. * All timings in Lumiera are based on integral datatypes * We use a fixed, very fine grid, something of the sort of microseconds - * The internal representation is based on a `typedef int64_t gavl_time_t` + * The internal representation is based on a `using raw_time_64 = int64_t` * We use a set of library routines and convenience-methods to - Get time in different formats (fractional seconds, frame counts) - Calculate with time values (overloaded operators) diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index 7531e988e..b4cee81b9 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -493,10 +493,10 @@ NOTE: At the time when Lumiera project started, the standard library provided Time ~~~~ Time values are represented by a family of opaque date types -with overloaded operators. The implementation is based on `gavl_time_t`, -an integral (µsec) time tick value. Thus, the arithmetic on time values -and time spans is limited and any Time handling and conversion is -centralised in library routines. +with overloaded operators. The implementation was inspired by `gavl_time_t` +and is thus based on a µ-seccond time grid, represented as 64-bit integer. +Thus, the arithmetic on time values and time spans is limited and any Time +handling and conversion is centralised in library routines. We distinguish between time values and a _quantisation_ into a frame or sample grid. In any case, quantisation has to be done once, explicitly diff --git a/src/lib/time.h b/src/lib/time.h index 3f1931738..17fa1ee0f 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -31,6 +31,12 @@ ** this model are defined to be a special kind of timecode, and thus dependent on ** a preceding time quantisation. ** + ** @deprecated 2025 this should not be a "simple" C library set aside from the Lumiera + ** time handling framework, rather it should be clarified that these are + ** implementation helpers and must not be used by any application code. + ** It should be checked which of these functions actually need to be + ** exposed through an interface header, since these are typically + ** used to implement parts of the time handling framework. ** ** @see lib::time::Time ** @see timequant.hpp @@ -43,11 +49,12 @@ #define LUMIERA_TIME_H #include -#include #ifdef __cplusplus /*=================== C++ facilities ===================== */ #include "lib/time/timevalue.hpp" +using lib::time::raw_time_64; + /** * Converts a fraction of seconds to Lumiera's internal opaque time scale. @@ -56,7 +63,7 @@ * here negative fractional micro-ticks are truncated towards zero. * This was deemed irrelevant in practice. */ -gavl_time_t +raw_time_64 lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds); @@ -66,7 +73,7 @@ lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds); * @note handles only positive frame counts and assumes the * origin to be at zero. */ -gavl_time_t +raw_time_64 lumiera_framecount_to_time (uint64_t frameCount, lib::time::FrameRate const& fps); @@ -75,7 +82,7 @@ lumiera_framecount_to_time (uint64_t frameCount, lib::time::FrameRate const& fps * @param framerate underlying framerate as rational number * @throw error::Logic on zero framerate */ -gavl_time_t +raw_time_64 lumiera_frame_duration (lib::time::FrameRate const& fps); @@ -91,15 +98,15 @@ extern "C" { /* ===================== C interface ======================== */ * The time grid used for quantisation is comprised of equally spaced intervals, * rooted at the given origin. The interval starting with the origin is numbered * as zero. Each interval includes its lower bound, but excludes its upper bound. - * @param grid spacing of the grid intervals, measured in GAVL_TIME_SCALE + * @param grid spacing of the grid intervals, measured in TimeValue::Scale (µ-ticks) * @return number of the grid interval containing the given time. * @warning the resulting value is limited to (Time::Min, Time::MAX) */ int64_t -lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid); +lumiera_quantise_frames (raw_time_64 time, raw_time_64 origin, raw_time_64 grid); int64_t -lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerate); +lumiera_quantise_frames_fps (raw_time_64 time, raw_time_64 origin, uint framerate); /** * Similar to #lumiera_quantise_frames, but returns a grid aligned _relative time_. @@ -110,17 +117,17 @@ lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerat * origin = Time::MIN, then all original time values above zero will be * clipped, because the result, relative to origin, needs to be <= Time::MAX */ -gavl_time_t -lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid); +raw_time_64 +lumiera_quantise_time (raw_time_64 time, raw_time_64 origin, raw_time_64 grid); /** * Calculate time of a grid point (frame start) * @param nr index number of the grid point (0 is at origin) - * @param grid spacing of the grid intervals, measured in GAVL_TIME_SCALE + * @param grid spacing of the grid intervals, measured in TimeValue::Scale (µ-ticks) * @return time point (frame start) on the Lumiera internal time scale */ -gavl_time_t -lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid); +raw_time_64 +lumiera_time_of_gridpoint (int64_t nr, raw_time_64 origin, raw_time_64 grid); /** * Build a time value by summing up the given components. @@ -129,7 +136,7 @@ lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid); * @param mins number of minutes * @param hours number of hours */ -gavl_time_t +raw_time_64 lumiera_build_time (long millis, uint secs, uint mins, uint hours); /** @@ -140,7 +147,7 @@ lumiera_build_time (long millis, uint secs, uint mins, uint hours); * @param mins number of minutes * @param hours number of hours */ -gavl_time_t +raw_time_64 lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours); /** @@ -148,59 +155,59 @@ lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours) * The components are interpreted as a NTSC drop-frame timecode. * @warning take care not to specify time codes that are illegal NTSC drop-frame times. */ -gavl_time_t +raw_time_64 lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours); /** Extract the hour part of given time. */ int -lumiera_time_hours (gavl_time_t time); +lumiera_time_hours (raw_time_64 time); /** Extract the minute part of given time. */ int -lumiera_time_minutes (gavl_time_t time); +lumiera_time_minutes (raw_time_64 time); /** Extract the seconds part of given time. */ int -lumiera_time_seconds (gavl_time_t time); +lumiera_time_seconds (raw_time_64 time); /** Extract the milliseconds part of given time. */ int -lumiera_time_millis (gavl_time_t time); +lumiera_time_millis (raw_time_64 time); /** * Extract the remaining frame part of given time. * @param fps frame rate (frames per second) */ int -lumiera_time_frames (gavl_time_t time, uint fps); +lumiera_time_frames (raw_time_64 time, uint fps); /** * Extract the frame part of given time, using NTSC drop-frame timecode. */ int -lumiera_time_ntsc_drop_frames (gavl_time_t time); +lumiera_time_ntsc_drop_frames (raw_time_64 time); /** * Extract the second part of given time, using NTSC drop-frame timecode. */ int -lumiera_time_ntsc_drop_seconds (gavl_time_t time); +lumiera_time_ntsc_drop_seconds (raw_time_64 time); /** * Extract the minute part of given time, using NTSC drop-frame timecode. */ int -lumiera_time_ntsc_drop_minutes (gavl_time_t time); +lumiera_time_ntsc_drop_minutes (raw_time_64 time); /** * Extract the hour part of given time, using NTSC drop-frame timecode. */ int -lumiera_time_ntsc_drop_hours (gavl_time_t time); +lumiera_time_ntsc_drop_hours (raw_time_64 time); /** @@ -210,7 +217,7 @@ lumiera_time_ntsc_drop_hours (gavl_time_t time); * by rendering into a suitable timecode format. */ char* -lumiera_tmpbuf_print_time (gavl_time_t time); +lumiera_tmpbuf_print_time (raw_time_64 time); diff --git a/src/lib/time/time.cpp b/src/lib/time/time.cpp index 51e54dc03..83d94783e 100644 --- a/src/lib/time/time.cpp +++ b/src/lib/time/time.cpp @@ -17,9 +17,9 @@ /** @file time.cpp ** Lumiera time handling core implementation unit. ** This translation unit generates code for the Lumiera internal time wrapper, - ** based on gavl_time_t, associated constants, marker classes for the derived - ** time entities (TimeVar, Offset, Duration, TimeSpan, FrameRate) and for the - ** basic time and frame rate conversion functions. + ** based on 64bit integral µ-tick values, associated constants, marker classes + ** for the derived time entities (TimeVar, Offset, Duration, TimeSpan, FrameRate) + ** and for the basic time and frame rate conversion functions. ** ** Client code includes either time.h (for basics and conversion functions) ** or timevalue.hpp (for the time entities), timequant.hpp for grid aligned @@ -75,11 +75,11 @@ namespace meta { namespace time { - const gavl_time_t TimeValue::SCALE = GAVL_TIME_SCALE; + const raw_time_64 TimeValue::SCALE = 1'000'000; /** @note the allowed time range is explicitly limited to help overflow protection */ - const Time Time::MAX ( TimeValue::buildRaw_(+std::numeric_limits::max() / 30) ); + const Time Time::MAX ( TimeValue::buildRaw_(+std::numeric_limits::max() / 30) ); const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) ); const Time Time::ZERO; @@ -94,8 +94,8 @@ namespace time { /** scale factor _used locally within this implementation header_. - * GAVL_TIME_SCALE rsp. TimeValue::SCALE is the correct factor or dividend when using - * gavl_time_t for display on a scale with seconds. Since we want to use milliseconds, + * TimeValue::SCALE (µ-ticks, i.e. 1e6) is the correct factor or dividend when using + * raw_time_64 for display on a scale with seconds. Since we want to use milliseconds, * we need to multiply or divide by 1000 to get correct results. */ #define TIME_SCALE_MS (lib::time::TimeValue::SCALE / 1000) @@ -138,7 +138,7 @@ namespace time { * of any "time-like" value, it is meant to be compact. */ TimeValue::operator string() const { - gavl_time_t time = t_; + raw_time_64 time = t_; int64_t millis, seconds; bool negative = (time < 0); @@ -163,7 +163,7 @@ namespace time { */ Time::operator string() const { - gavl_time_t time = t_; + raw_time_64 time = t_; int millis, seconds, minutes, hours; bool negative = (time < 0); @@ -233,7 +233,7 @@ namespace time { /** @internal backdoor to sneak in a raw time value * bypassing any normalisation and limiting */ TimeValue - TimeValue::buildRaw_ (gavl_time_t raw) + TimeValue::buildRaw_ (raw_time_64 raw) { return reinterpret_cast (raw); } @@ -326,7 +326,7 @@ namespace time { { boost::rational distance (this->t_); distance *= factor; - gavl_time_t microTicks = floordiv (distance.numerator(), distance.denominator()); + raw_time_64 microTicks = floordiv (distance.numerator(), distance.denominator()); return Offset{buildRaw_(microTicks)}; } @@ -337,7 +337,7 @@ namespace time { { double distance(this->t_); distance *= factor; - gavl_time_t microTicks = floor (distance); + raw_time_64 microTicks = floor (distance); return Offset{buildRaw_(microTicks)}; } @@ -383,7 +383,7 @@ namespace util { char* -lumiera_tmpbuf_print_time (gavl_time_t time) +lumiera_tmpbuf_print_time (raw_time_64 time) { int milliseconds, seconds, minutes, hours; bool negative = (time < 0); @@ -409,7 +409,7 @@ lumiera_tmpbuf_print_time (gavl_time_t time) /// @todo this utility function could be factored out into a `FSecs` or `RSec` class ///////////////////////TICKET #1262 -gavl_time_t +raw_time_64 lumiera_rational_to_time (FSecs const& fractionalSeconds) { // avoid numeric wrap from values not representable as 64bit µ-ticks @@ -417,22 +417,22 @@ lumiera_rational_to_time (FSecs const& fractionalSeconds) return (fractionalSeconds < 0? -1:+1) * std::numeric_limits::max(); - return gavl_time_t(util::reQuant (fractionalSeconds.numerator() + return raw_time_64(util::reQuant (fractionalSeconds.numerator() ,fractionalSeconds.denominator() ,lib::time::TimeValue::SCALE )); } -gavl_time_t +raw_time_64 lumiera_framecount_to_time (uint64_t frameCount, FrameRate const& fps) { // convert to 64bit boost::rational framerate (fps.numerator(), fps.denominator()); - return rational_cast (lib::time::TimeValue::SCALE * frameCount / framerate); + return rational_cast (lib::time::TimeValue::SCALE * frameCount / framerate); } -gavl_time_t +raw_time_64 lumiera_frame_duration (FrameRate const& fps) { if (!fps) @@ -447,20 +447,20 @@ lumiera_frame_duration (FrameRate const& fps) namespace { // implementation: basic frame quantisation.... inline int64_t - calculate_quantisation (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) + calculate_quantisation (raw_time_64 time, raw_time_64 origin, raw_time_64 grid) { time -= origin; return floordiv (time,grid); } inline int64_t - calculate_quantisation (gavl_time_t time, gavl_time_t origin, uint framerate, uint framerate_divisor=1) + calculate_quantisation (raw_time_64 time, raw_time_64 origin, uint framerate, uint framerate_divisor=1) { REQUIRE (framerate); REQUIRE (framerate_divisor); - const int64_t limit_num = std::numeric_limits::max() / framerate; - const int64_t limit_den = std::numeric_limits::max() / framerate_divisor; + const int64_t limit_num = std::numeric_limits::max() / framerate; + const int64_t limit_den = std::numeric_limits::max() / framerate_divisor; const int64_t microScale {lib::time::TimeValue::SCALE}; // protect against numeric overflow @@ -474,7 +474,7 @@ namespace { // implementation: basic frame quantisation.... { // direct calculation will overflow. // use the less precise method instead... - gavl_time_t frameDuration = microScale / framerate; // truncated to µs + raw_time_64 frameDuration = microScale / framerate; // truncated to µs return calculate_quantisation (time,origin, frameDuration); } } @@ -482,37 +482,37 @@ namespace { // implementation: basic frame quantisation.... int64_t -lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) +lumiera_quantise_frames (raw_time_64 time, raw_time_64 origin, raw_time_64 grid) { return calculate_quantisation (time, origin, grid); } int64_t -lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerate) +lumiera_quantise_frames_fps (raw_time_64 time, raw_time_64 origin, uint framerate) { return calculate_quantisation (time, origin, framerate); } -gavl_time_t -lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) +raw_time_64 +lumiera_quantise_time (raw_time_64 time, raw_time_64 origin, raw_time_64 grid) { int64_t count = calculate_quantisation (time, origin, grid); - gavl_time_t alignedTime = count * grid; + raw_time_64 alignedTime = count * grid; return alignedTime; } -gavl_time_t -lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid) +raw_time_64 +lumiera_time_of_gridpoint (int64_t nr, raw_time_64 origin, raw_time_64 grid) { - gavl_time_t offset = nr * grid; + raw_time_64 offset = nr * grid; return origin + offset; } -gavl_time_t +raw_time_64 lumiera_build_time(long millis, uint secs, uint mins, uint hours) { - gavl_time_t time = millis + raw_time_64 time = millis + 1000 * secs + 1000 * 60 * mins + 1000 * 60 * 60 * hours; @@ -520,10 +520,10 @@ lumiera_build_time(long millis, uint secs, uint mins, uint hours) return time; } -gavl_time_t +raw_time_64 lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours) { - gavl_time_t time = 1000LL * frames/fps + raw_time_64 time = 1000LL * frames/fps + 1000 * secs + 1000 * 60 * mins + 1000 * 60 * 60 * hours; @@ -532,31 +532,31 @@ lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours) } int -lumiera_time_hours (gavl_time_t time) +lumiera_time_hours (raw_time_64 time) { return time / TIME_SCALE_MS / 1000 / 60 / 60; } int -lumiera_time_minutes (gavl_time_t time) +lumiera_time_minutes (raw_time_64 time) { return (time / TIME_SCALE_MS / 1000 / 60) % 60; } int -lumiera_time_seconds (gavl_time_t time) +lumiera_time_seconds (raw_time_64 time) { return (time / TIME_SCALE_MS / 1000) % 60; } int -lumiera_time_millis (gavl_time_t time) +lumiera_time_millis (raw_time_64 time) { return (time / TIME_SCALE_MS) % 1000; } int -lumiera_time_frames (gavl_time_t time, uint fps) +lumiera_time_frames (raw_time_64 time, uint fps) { REQUIRE (fps < uint(std::numeric_limits::max())); return floordiv (lumiera_time_millis(time) * int(fps), TIME_SCALE_MS); @@ -581,7 +581,7 @@ namespace { // implementation helper * @todo I doubt this works correct for negative times!! */ inline int64_t - calculate_drop_frame_number (gavl_time_t time) + calculate_drop_frame_number (raw_time_64 time) { int64_t frameNr = calculate_quantisation (time, 0, 30000, 1001); @@ -599,30 +599,30 @@ namespace { // implementation helper } int -lumiera_time_ntsc_drop_frames (gavl_time_t time) +lumiera_time_ntsc_drop_frames (raw_time_64 time) { return calculate_drop_frame_number(time) % 30; } int -lumiera_time_ntsc_drop_seconds (gavl_time_t time) +lumiera_time_ntsc_drop_seconds (raw_time_64 time) { return calculate_drop_frame_number(time) / 30 % 60; } int -lumiera_time_ntsc_drop_minutes (gavl_time_t time) +lumiera_time_ntsc_drop_minutes (raw_time_64 time) { return calculate_drop_frame_number(time) / 30 / 60 % 60; } int -lumiera_time_ntsc_drop_hours (gavl_time_t time) +lumiera_time_ntsc_drop_hours (raw_time_64 time) { return calculate_drop_frame_number(time) / 30 / 60 / 60 % 24; } -gavl_time_t +raw_time_64 lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours) { uint64_t total_mins = 60 * hours + mins; @@ -631,7 +631,7 @@ lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours) + 30 * secs + frames - 2 * (total_mins - total_mins / 10); - gavl_time_t result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC); + raw_time_64 result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC); if (0 != result) // compensate for truncating down on conversion result += 1; // without this adjustment the frame number diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index e9034c5a4..e9a3fa794 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -350,7 +350,7 @@ namespace time { /** */ Secs::Secs (QuTime const& quantisedTime) : TCode(quantisedTime) -// : sec_(TimeVar(quantisedTime) / GAVL_TIME_SCALE) //////////////////////////////////////////////////////TICKET #736 implement Seconds format +// : sec_(TimeVar(quantisedTime) / TimeValue::SCALE) /////////////////////////////////////////////////////TICKET #736 implement Seconds format { } diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 289cf4b6a..62e4fe086 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -14,7 +14,8 @@ /** @file timevalue.hpp ** a family of time value like entities and their relationships. ** This is the foundation for the Lumiera time handling framework. On the implementation - ** level, time values are represented as 64bit integer values `gavl_time_t`. But for the + ** level, time values are represented as 64bit integer values `raw_time_64`, similar to + ** and inspired by `gavl_time_t` from the raw-video handling [Lib-GAVL]. But for the ** actual use, we create several kinds of time "values", based on their logical properties. ** These time values are considered to be fixed (immutable) values, which may only be ** created through some limited construction paths, and any time based calculation @@ -30,7 +31,7 @@ ** way to retrieve such a value is by formatting it into a time code format. ** ** The lib::time::TimeValue serves as foundation for all further time calculations; - ** in fact it is implemented as a single 64bit µ-tick value (`gavl_time_t`). The + ** in fact it is implemented as a single 64bit µ-tick value (`raw_time_64`). The ** further time entities are implemented as value objects (without virtual functions): ** - lib::time::Time represents a time instant and is the reference for any usage ** - lib::time::TimeVar is a mutable time variable and can be used for calculations @@ -86,7 +87,8 @@ ** @see time.h basic time calculation library functions ** @see timequant.hpp ** @see TimeValue_test - ** + ** + ** [Lib-GAVL]: https://github.com/bplaum/gavl */ @@ -100,11 +102,6 @@ #include #include -extern "C" { -#include -#include -} - namespace lib { namespace time { @@ -118,11 +115,22 @@ namespace time { class Mutation; + /** + * Raw µ-tick time representation used in Lumiera. + * @remark this representation was inspired by [Lib-GAVL]. + * @todo 2025 a mere type alias is up to debate -- very likely we'll use a wrapper soon /////////////////TICKET #1258 + * @warning application logic should avoid handling any raw time value + * directly and rather treat time data as an opaque entity. + * [Lib-GAVL]: https://github.com/bplaum/gavl + */ + using raw_time_64 = int64_t; + + /** * basic constant internal time value. * These time values provide the implementation base * for all further time types. They can be created by - * wrapping up a raw micro tick value (gavl_time_t), + * wrapping up a raw micro tick value (raw_time_64), * are totally ordered, but besides that, * they are opaque and non-mutable. * @note clients should prefer to use Time instances, @@ -132,12 +140,12 @@ namespace time { */ class TimeValue : boost::totally_ordered> + boost::totally_ordered> { protected: /** the raw (internal) time value * used to implement the time types */ - gavl_time_t t_; + raw_time_64 t_; /** Assigning of time values is not allowed, @@ -153,9 +161,9 @@ namespace time { friend class Mutation; /** explicit limit of allowed time range */ - static gavl_time_t limitedTime (gavl_time_t raw); + static raw_time_64 limitedTime (raw_time_64 raw); /** safe calculation of explicitly limited time offset */ - static gavl_time_t limitedDelta (gavl_time_t origin, gavl_time_t target); + static raw_time_64 limitedDelta (raw_time_64 origin, raw_time_64 target); /** @internal for Offset and Duration entities built on top */ TimeValue (TimeValue const& origin, TimeValue const& target) @@ -164,11 +172,11 @@ namespace time { public: /** Number of micro ticks (µs) per second as basic time scale */ - static const gavl_time_t SCALE; + static const raw_time_64 SCALE; explicit - TimeValue (gavl_time_t val) ///< time given in µ ticks here + TimeValue (raw_time_64 val) ///< time given in µ ticks here : t_{limitedTime (val)} { } @@ -178,9 +186,9 @@ namespace time { { } /** @internal to pass Time values to C functions */ - friend gavl_time_t _raw (TimeValue const& time) { return time.t_; } + friend raw_time_64 _raw (TimeValue const& time) { return time.t_; } friend HashVal hash_value (TimeValue const&); - static TimeValue buildRaw_(gavl_time_t); + static TimeValue buildRaw_(raw_time_64); /** @internal diagnostics */ operator std::string () const; @@ -190,10 +198,10 @@ namespace time { // Supporting totally_ordered friend bool operator< (TimeValue const& t1, TimeValue const& t2) { return t1.t_ < t2.t_; } - friend bool operator< (TimeValue const& t1, gavl_time_t t2) { return t1.t_ < t2 ; } - friend bool operator> (TimeValue const& t1, gavl_time_t t2) { return t1.t_ > t2 ; } + friend bool operator< (TimeValue const& t1, raw_time_64 t2) { return t1.t_ < t2 ; } + friend bool operator> (TimeValue const& t1, raw_time_64 t2) { return t1.t_ > t2 ; } friend bool operator== (TimeValue const& t1, TimeValue const& t2) { return t1.t_ == t2.t_; } - friend bool operator== (TimeValue const& t1, gavl_time_t t2) { return t1.t_ == t2 ; } + friend bool operator== (TimeValue const& t1, raw_time_64 t2) { return t1.t_ == t2 ; } }; @@ -207,7 +215,7 @@ namespace time { /** relative framecount or frame number. * Used within the engine at places where the underlying * grid and origin is obvious from the call context. - * @warning do not mix up gavl_time_t and FrameCnt. + * @warning do not mix up raw_time_64 and FrameCnt. * @warning use 64bit consistently. * beware: `long` is 32bit on i386 * @note any conversion to frame numbers should go through @@ -216,7 +224,7 @@ namespace time { using FrameCnt = int64_t; /** rational representation of fractional seconds - * @warning do not mix up gavl_time_t and FSecs */ + * @warning do not mix up raw_time_64 and FSecs */ using FSecs = boost::rational; @@ -226,7 +234,7 @@ namespace time { * allowing copy and re-accessing * @note supports scaling by a factor, * which _deliberately_ is chosen - * as int, not gavl_time_t, because the + * as int, not raw_time_64, because the * multiplying of times is meaningless. */ class TimeVar @@ -258,7 +266,7 @@ namespace time { } /// Support mixing with plain 64bit int arithmetics - operator gavl_time_t() const { return t_; } + operator raw_time_64() const { return t_; } /// Support for micro-tick precise time arithmetics operator FSecs() const { return FSecs{t_, TimeValue::SCALE}; } @@ -708,8 +716,8 @@ namespace time { return n; } - inline gavl_time_t - symmetricLimit (gavl_time_t raw, TimeValue lim) + inline raw_time_64 + symmetricLimit (raw_time_64 raw, TimeValue lim) { return raw > lim? _raw(lim) : -raw > lim? -_raw(lim) @@ -744,21 +752,21 @@ namespace time { * While Time entities are \c not a "safeInt" * implementation, we limit new values to * lower the likelihood of wrap-around */ - inline gavl_time_t - TimeValue::limitedTime (gavl_time_t raw) + inline raw_time_64 + TimeValue::limitedTime (raw_time_64 raw) { return symmetricLimit (raw, Time::MAX); } - inline gavl_time_t - TimeValue::limitedDelta (gavl_time_t origin, gavl_time_t target) + inline raw_time_64 + TimeValue::limitedDelta (raw_time_64 origin, raw_time_64 target) { if (0 > (origin^target)) {// prevent possible numeric wrap origin = symmetricLimit (origin, Duration::MAX); target = symmetricLimit (target, Duration::MAX); } - gavl_time_t res = target - origin; + raw_time_64 res = target - origin; return symmetricLimit (res, Duration::MAX); } diff --git a/src/stage/widget/timecode-widget.cpp b/src/stage/widget/timecode-widget.cpp index cf387f225..fbf02f74e 100644 --- a/src/stage/widget/timecode-widget.cpp +++ b/src/stage/widget/timecode-widget.cpp @@ -29,7 +29,7 @@ #include "stage/widget/timecode-widget.hpp" - +#include "lib/time/timevalue.hpp" #include "lib/time/diagnostics.hpp" ////////////TODO: temporary solution to get H:M:S components. Use TimeCode instead! #include @@ -40,9 +40,6 @@ #include #include -#include -#include - using boost::lexical_cast; using sigc::mem_fun; @@ -433,7 +430,7 @@ namespace widget { // void // TimeCode::smpte_offset_changed() // { - // gavl_time_t current; + // raw_time_64 current; // switch (_mode) { // case SMPTE: @@ -1278,7 +1275,7 @@ namespace widget { Time TimeCode::audio_time_from_display () const { - gavl_time_t parsedAudioFrames = lexical_cast(audio_frames_label.get_text()); + raw_time_64 parsedAudioFrames = lexical_cast(audio_frames_label.get_text()); return Time(TimeValue(parsedAudioFrames)); } diff --git a/src/steam/control/media-impl-lib.hpp b/src/steam/control/media-impl-lib.hpp index e797469c5..7c52f3bb7 100644 --- a/src/steam/control/media-impl-lib.hpp +++ b/src/steam/control/media-impl-lib.hpp @@ -21,6 +21,10 @@ ** the referenced libraries, though. This will be done with adapter implementations, ** where the interface MediaImplLip defines those aspects actually used and required ** by our internal processing. + ** @deprecated 2025 -- this is an architecture draft, and shows the direction to take + ** for the integration with external libraries. Yet in reality, this task + ** can not conceivably be thus simple; take everything here with a grain + ** of salt (since this code is not yet actually used in any way). */ diff --git a/src/steam/external/libgavl.hpp b/src/steam/external/libgavl.hpp index 2e09c056b..e5b7f45f8 100644 --- a/src/steam/external/libgavl.hpp +++ b/src/steam/external/libgavl.hpp @@ -13,11 +13,31 @@ /** @file libgavl.hpp - ** Concrete implementation of the MediaImplLib facade to work with `libGAVL` + ** Concrete implementation of the MediaImplLib facade to work with [lib-GAVL], + ** a library for handling basic raw video data formats. ** @todo a draft and placeholder code from 2008, at which time the intention was ** to rely on libGAVL for processing of raw media data. This seemed like a ** good idea at that time, but we should re-evaluate if libGAVL is maintained - ** and in actual use, before we start really relying on it + ** and in actual use, before we start really relying on it. + ** @todo 2025 this code is a kind of _architecture placeholder_ — it is clear that + ** a solution can can not be _that simple_, but any viable solution will + ** rely onto an approach like outlined here: provide a façade interface, + ** which has to be implemented by any plug-in component to provide access + ** to the processing capabilities of a specific library. The access point + ** functions exposed on the facade however must be shaped by the specific + ** way how the builder and the render engine interacts with and delegates + ** back to the media-handling library. Such an interface can not be based + ** on first principles alone. + ** @todo 2025 I still retain the dependency on lib-GAVL for now, even while we + ** never got into actually using it. Simply because I still consider this + ** library exemplary, and would love to use it for real. Yet it remains + ** to be seen if the project actually gets a chance to do so, simply + ** because FFmpeg roughly covers the same ground. Presumably it turns + ** out to be a basic requirement to ship a plug-in with FFmpeg binding. + ** Furthermore, one especially tricky aspect is how to support displays + ** with extended dynamic range -- a requirement which has the potential + ** to blur the notion of »raw video«... + ** [Lib-GAVL]: https://github.com/bplaum/gavl */ diff --git a/src/vault/gear/block-flow.hpp b/src/vault/gear/block-flow.hpp index bb29f7afc..8a7bc7230 100644 --- a/src/vault/gear/block-flow.hpp +++ b/src/vault/gear/block-flow.hpp @@ -434,7 +434,7 @@ namespace gear { adjustEpochStep (double factor) { double stretched = _raw(epochStep_) * factor; - gavl_time_t microTicks(floor (stretched)); + raw_time_64 microTicks(floor (stretched)); epochStep_ = TimeValue{microTicks}; } diff --git a/src/vault/gear/job.h b/src/vault/gear/job.h index 4f913fc20..506878561 100644 --- a/src/vault/gear/job.h +++ b/src/vault/gear/job.h @@ -124,7 +124,7 @@ union InvocationInstanceID */ struct lumiera_jobParameter_struct { - gavl_time_t nominalTime; /////////////////////////////////////////////////////////////////////////TICKET #1295 job invocation parameter: framework to interpret this time + raw_time_64 nominalTime; /////////////////////////////////////////////////////////////////////////TICKET #1295 job invocation parameter: framework to interpret this time InvocationInstanceID invoKey; //////////////////////////////////////////////////////////////TODO: place an additional parameter value here, or make the instanceID globally unique? ////////////////////////////////////////////////////////////////////////////////////TICKET #1293 job invocation identity @@ -166,7 +166,7 @@ typedef lumiera_jobDefinition* LumieraJobDefinition; */ struct lumiera_jobDescriptor_struct { - gavl_time_t deadline; ///< given in real wall clock time + raw_time_64 deadline; ///< given in real wall clock time JobState jobState; lumiera_jobDefinition jobDefinition; ///< of interest only to Steam-Layer diff --git a/src/vault/gear/load-controller.hpp b/src/vault/gear/load-controller.hpp index e0df1b311..afc106815 100644 --- a/src/vault/gear/load-controller.hpp +++ b/src/vault/gear/load-controller.hpp @@ -362,7 +362,7 @@ namespace gear { { auto scatter = [&](Duration horizon) { - gavl_time_t wrap = hash_value(now) % _raw(horizon); + lib::time::raw_time_64 wrap = hash_value(now) % _raw(horizon); ENSURE (0 <= wrap and wrap < _raw(horizon)); return TimeValue{wrap}; }; diff --git a/tests/basics/time/time-basics-test.cpp b/tests/basics/time/time-basics-test.cpp index 313d17285..531dbea58 100644 --- a/tests/basics/time/time-basics-test.cpp +++ b/tests/basics/time/time-basics-test.cpp @@ -101,7 +101,7 @@ namespace test{ CHECK (!(var < ref) ); CHECK ( (var > ref) ); - gavl_time_t gat(var); + raw_time_64 gat(var); CHECK (!(gat == ref) ); CHECK ( (gat != ref) ); CHECK ( (gat >= ref) ); diff --git a/tests/basics/time/time-control-test.cpp b/tests/basics/time/time-control-test.cpp index bdd2876ee..46480d009 100644 --- a/tests/basics/time/time-control-test.cpp +++ b/tests/basics/time/time-control-test.cpp @@ -123,13 +123,13 @@ namespace test{ */ class TimeControl_test : public Test { - gavl_time_t + raw_time_64 random_or_get (string arg) { if (isnil(arg)) - return gavl_time_t(1 + rani (100000)) * TimeValue::SCALE; + return raw_time_64(1 + rani (100000)) * TimeValue::SCALE; else - return lexical_cast (arg); + return lexical_cast (arg); } diff --git a/tests/basics/time/time-mutation-test.cpp b/tests/basics/time/time-mutation-test.cpp index 1ccb38e6b..a5e5a44d7 100644 --- a/tests/basics/time/time-mutation-test.cpp +++ b/tests/basics/time/time-mutation-test.cpp @@ -61,13 +61,13 @@ namespace test{ */ class TimeMutation_test : public Test { - gavl_time_t + raw_time_64 random_or_get (string arg) { if (isnil(arg)) - return gavl_time_t(1 + rani (100000)) * TimeValue::SCALE; + return raw_time_64(1 + rani (100000)) * TimeValue::SCALE; else - return lexical_cast (arg); + return lexical_cast (arg); } struct TestValues diff --git a/tests/basics/time/time-value-test.cpp b/tests/basics/time/time-value-test.cpp index e25aed54a..861d75144 100644 --- a/tests/basics/time/time-value-test.cpp +++ b/tests/basics/time/time-value-test.cpp @@ -44,7 +44,7 @@ namespace test{ */ class TimeValue_test : public Test { - gavl_time_t + raw_time_64 random_or_get (Arg arg) { if (isnil(arg)) @@ -53,7 +53,7 @@ namespace test{ return 1 + rani(10000); } else - return lexical_cast (arg[1]); + return lexical_cast (arg[1]); } @@ -101,7 +101,7 @@ namespace test{ CHECK (val < max); // mixed comparisons with raw numeric time - gavl_time_t g2 (-2); + raw_time_64 g2 (-2); CHECK (zero > g2); CHECK (one > g2); CHECK (one >= g2); @@ -142,7 +142,7 @@ namespace test{ CHECK (var < Time::MAX); CHECK (var > Time::MIN); - gavl_time_t raw (var); + raw_time_64 raw (var); CHECK (raw == org); CHECK (raw > org - two); diff --git a/tests/core/steam/engine/job-planning-pipeline-test.cpp b/tests/core/steam/engine/job-planning-pipeline-test.cpp index cae328607..3df8f7809 100644 --- a/tests/core/steam/engine/job-planning-pipeline-test.cpp +++ b/tests/core/steam/engine/job-planning-pipeline-test.cpp @@ -235,7 +235,7 @@ namespace test { { Time frame{pipeline.currPoint}; // can access the embedded PipeFrameTick core to get "currPoint" (nominal time) Job job = pipeline->ticket().createJobFor(frame); // looking always at the second element, which is the current JobTicket - TimeValue nominalTime{job.parameter.nominalTime}; // job parameter holds the microseconds (gavl_time_t) + TimeValue nominalTime{job.parameter.nominalTime}; // job parameter holds the microseconds (raw_time_64) int32_t mark = job.parameter.invoKey.part.a; // the MockDispatcher places the given "mark" here return _Fmt{"J(%d|%s)"} % mark % nominalTime; }; @@ -309,7 +309,7 @@ namespace test { auto visualise = [](auto& pipeline) -> string { Job job = pipeline.buildJob(); // let the JobPlanning construct the »current job« - TimeValue nominalTime{job.parameter.nominalTime}; // job parameter holds the microseconds (gavl_time_t) + TimeValue nominalTime{job.parameter.nominalTime}; // job parameter holds the microseconds (raw_time_64) int32_t mark = job.parameter.invoKey.part.a; // the MockDispatcher places the given "mark" here TimeValue deadline{pipeline.determineDeadline()}; return _Fmt{"J(%d|%s⧐%s)"} diff --git a/tests/library/c-lib/test-time.c b/tests/library/c-lib/test-time.c index 511bdbc00..c491ef765 100644 --- a/tests/library/c-lib/test-time.c +++ b/tests/library/c-lib/test-time.c @@ -20,13 +20,18 @@ #include "lib/test/test.h" -#include "lib/time.h" +#include #include #include +typedef int64_t raw_time_64; //////////////////////////////////////////////////////////////////////////////TICKET #1259 turn time.h in to a C++ implementation header + +#include "lib/time.h" /////////////////////////////////////////////////////////////////////////////////////TICKET #1259 this should not be a general purpose library, but rather an implementation base + + static int -calculate_framecount (gavl_time_t t, uint fps) +calculate_framecount (raw_time_64 t, uint fps) { return lumiera_quantise_frames_fps (t,0,fps); } @@ -48,9 +53,9 @@ const int FPS = 24; TEST (basic) { // Zero - gavl_time_t t = lumiera_build_time (0,0,0,0); + raw_time_64 t = lumiera_build_time (0,0,0,0); - CHECK ((gavl_time_t) t == 0); + CHECK ((raw_time_64) t == 0); CHECK (lumiera_time_millis (t) == 0); CHECK (lumiera_time_seconds (t) == 0); CHECK (lumiera_time_minutes (t) == 0); @@ -83,7 +88,7 @@ TEST (basic) TEST (fps) { - gavl_time_t t = lumiera_build_time_fps (FPS, FRAMES, SECONDS, MINUTES, HOURS); + raw_time_64 t = lumiera_build_time_fps (FPS, FRAMES, SECONDS, MINUTES, HOURS); CHECK (lumiera_time_millis (t) == FRAMES * 1000 / FPS); CHECK (lumiera_time_seconds (t) == SECONDS); @@ -102,9 +107,9 @@ TEST (fps) TEST (ntsc_drop_frame) { // Make sure frame 0 begins at 0 - gavl_time_t t = lumiera_build_time_ntsc_drop (0, 0, 0, 0); + raw_time_64 t = lumiera_build_time_ntsc_drop (0, 0, 0, 0); - CHECK ((gavl_time_t) t == 0); + CHECK ((raw_time_64) t == 0); CHECK (lumiera_time_millis (t) == 0); CHECK (lumiera_time_seconds (t) == 0); CHECK (lumiera_time_minutes (t) == 0); diff --git a/tests/stage/model/zoom-window-test.cpp b/tests/stage/model/zoom-window-test.cpp index 0681de868..1206540db 100644 --- a/tests/stage/model/zoom-window-test.cpp +++ b/tests/stage/model/zoom-window-test.cpp @@ -67,6 +67,7 @@ namespace stage{ namespace model{ namespace test { + using lib::time::raw_time_64; namespace { // simplified notation for expected results... @@ -577,7 +578,7 @@ namespace test { CHECK (2_r/3 < poison and poison < 1); // looks innocuous... CHECK (poison + Time::SCALE < 0); // simple calculations fail due to numeric overflow CHECK (poison * Time::SCALE < 0); - CHECK (-6 == rational_cast(poison * Time::SCALE)); // naive conversion to µ-ticks would lead to overflow + CHECK (-6 == rational_cast(poison * Time::SCALE)); // naive conversion to µ-ticks would lead to overflow CHECK (671453 == _raw(Time(FSecs(poison)))); // however the actual conversion routine is safeguarded CHECK (671453.812f == rational_cast(poison)*Time::SCALE); @@ -667,7 +668,7 @@ namespace test { CHECK (win.overallSpan().duration() == TimeValue{307445734561825860}); // However, all base values turn out unaffected CHECK (win.visible().duration() == TimeValue{856350691}); - TimeValue targetPos{gavl_time_t(_raw(win.overallSpan().duration()) // based on the overall span... + TimeValue targetPos{raw_time_64(_raw(win.overallSpan().duration()) // based on the overall span... * rational_cast (poison))}; // the given toxic factor would point at that target position CHECK (targetPos == TimeValue{206435633551724864}); diff --git a/tests/vault/gear/block-flow-test.cpp b/tests/vault/gear/block-flow-test.cpp index 5ed2e56b3..1d672d6ad 100644 --- a/tests/vault/gear/block-flow-test.cpp +++ b/tests/vault/gear/block-flow-test.cpp @@ -339,7 +339,7 @@ namespace test { { auto N = AVERAGE_EPOCHS; auto averageTicks = double(_raw(old))*(N-1)/N + contribution/N; - return TimeValue{gavl_time_t (floor (averageTicks))}; + return TimeValue{raw_time_64 (floor (averageTicks))}; }; TimeVar step = bFlow.getEpochStep(); @@ -396,8 +396,8 @@ namespace test { { const size_t FPS = 200; const size_t TICK_P_S = FPS * ACTIVITIES_P_FR; // Simulated throughput 200 frames per second - const gavl_time_t STP = Time::SCALE / TICK_P_S; // Simulation stepping (here 2 steps per ms) - const gavl_time_t RUN = _raw(Time{0,0,3}); // nominal length of the simulation time axis + const raw_time_64 STP = Time::SCALE / TICK_P_S; // Simulation stepping (here 2 steps per ms) + const raw_time_64 RUN = _raw(Time{0,0,3}); // nominal length of the simulation time axis Offset BASE_DEADLINE{FSecs{1,2}}; // base pre-roll before deadline Offset SPREAD_DEAD{FSecs{2,100}}; // random spread of deadline around base const uint INVOKE_LAG = _raw(Time{250,0}) /STP; // „invoke“ the Activity after simulated 250ms (≙ 500 steps) diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 206eaaf68..badba315c 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -129698,8 +129698,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -159479,18 +159479,21 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - + - + - + + +

Christian kannte bereits Burkart Pflaum, und hat uns auf dessen Libraries aufmerksam gemacht. Die Libraries sind vorbildlich in iherer Struktur und in dem beschränkten Scope. Wahrscheinlich sind sie problemloser einzubinden als FFmpeg. Aber das Kernproblem bleibt: das ist eine one-Man-Show, und wir werden trotzdem auf FFmpeg nicht verzichten können....

-
+ + @@ -162285,7 +162288,8 @@ Since then others have made contributions, see the log for the history.
- + + @@ -162713,8 +162717,7 @@ Since then others have made contributions, see the log for the history. - - + @@ -162775,8 +162778,7 @@ Since then others have made contributions, see the log for the history. - - + @@ -162785,10 +162787,11 @@ Since then others have made contributions, see the log for the history.
- + - - + + + @@ -162801,8 +162804,9 @@ Since then others have made contributions, see the log for the history. + - + @@ -162812,6 +162816,98 @@ Since then others have made contributions, see the log for the history. + + + + + + + + + + + + + + + + + + + + +

+ raw_time_64 +

+ + +
+
+ + + + + + +

+ schon seit langem will ich von der "C-Time-Library" weg, denn diese ist gradezu eine Einladung, Timecode-Operationen ad-hoc zu implementieren +

+ + +
+
+ + + + + + + + + + +

+ ....weil eine solche Library gradezu dazu einläd, sich nicht mit den „mühsamen“ Abstraktionen des Time-Handling-Frameworks herumzuschlagen, sondern stattessen einfach mit Zahlen zu rechnen. +

+ + +
+
+ + + + + + +

+ ...möglicherweise lassen sich diese Funktionen nämlich in Gruppen einteilen und dann direkt in einen anonymen namespace in die jeweilige Translation-Unit schieben.... +

+ + +
+
+ + + + + + + + +

+ Der Test täuscht: weil er in C geschrieben ist, sieht er so komplex aus. Dabei besteht die erste Hälfte lediglich draus, eine Konstruktor-Funktion zu testen. Der sinnvolle Kern daran ist die decimator-Sequenz zum Herunterbrechen von Zeiten. Die wird hier aber nur oberflächlich getestet. Da sollte man, wenn schon, wirklich auf die Grenzfälle losgehen ⟹ Test neu schreiben! +

+

+ +

+

+ Der zweite Teil besteht im Antesten der drop-Frame-Umwandlung ⟹ auch das in eigenständigen neuen Test packen +

+ + +
+
+
@@ -163898,6 +163994,15 @@ Since then others have made contributions, see the log for the history.
+ + + + + + + + +