Option to disable SMPTE Rate Check
As the industry moves towards faster and faster rates for various media (headsets, immersive material). We're seeing content rise above the 60fps ceiling, for better or worse, that the SMPTE is currently limiting us to. Because of this, our editorial workflow is hampered by the fact that we get an error while trying to convert/conform 90fps material.
https://github.com/AcademySoftwareFoundation/OpenTimelineIO/blob/ad449d8122e778e80e57ac7e6b1b1a38afe7f91b/src/opentime/rationalTime.cpp#L326
We've written around this problem with custom TimeCode objects, but I'm wondering if it will be a strict policy forever and we should wait for SMPTE to update their standards, or if there's wiggle room for opt-in/out of the explicit rate checking. RationalTime supports any rate, so I would like to assume timecode could as well, even if there's a toggle or optional preference for how strict it should be.
I'm trying to build OTIO into a non-timeline tool to handle better EDL parsing for metadata and a good half of my sample ALE EDLs from 10 years ago fail due to this check. Would love to disable this behavior.
It looks like SMPTE 12-3 covers rates up to 120 (with and without drop frame fractional variants). https://pub.smpte.org/doc/12/
Side note: the SMPTE document URL in our source code is 404: https://github.com/AcademySoftwareFoundation/OpenTimelineIO/blob/2429dd5cd35a16949b7b675c24aad61e123f064d/src/opentime/rationalTime.cpp#L23
After a discussion with the Technical Steering Committee - we felt that what we should do is:
- For
from_framesandfrom_timecode- theratearg should "snap" values that are nearly SMPTE rates (n / 1001) to the SMPTE rate from_timecodeshould have an optional arg to allow you to disable all snapping and validation on the ratefrom_framesshould have an optional arg to allow you to disable snapping- Add more of the commonly used rates that may not be part of any specification to the list of ones the function accepts
When we talk about values that are "nearly" smpte rates, we'd have to talk about the exact logic, but it'd be something like within 0.01 of the n / 1001 variant of the rounded rate - the goal would be to make sure it properly snaps values like 23.98 to 24000/1001.
We'd also love for people to share what rates they commonly see used with timecode that aren't in the list we have so we can make sure the list reflects real workflows but is still acting as a reasonable guard against erroneous data.
We feel like this:
- Makes the default behavior of the library match what people expect (i.e. treat things like 23.98 and 59.94 as an"aliases" for rates like 24000 / 1001 and 60000 / 1001)
- Gives users a tool to they can use when they want to manage snapping behavior themselves or work with unusual frame rates and make OTIO just take on the math part
@mccartnm - do you feel like this would address your use cases well?
@reinecke these options sounds great! Snapping is a great default. I think 0.01 is a reasonable epsilon for this sort of thing. Won't break down with general floating point error accum.
constexpr double s_smpte_snapping_epsilon = 0.01;
double snap_smpte_rate(double rate, double &snapped) {
snapped = nearest_smpte_timecode_rate(rate);
return std::abs(rate - snapped);
}
// ...
double snapped;
double delta = snap_smpte_rate(rate, snapped);
if (delta < s_smpte_snapping_epsilon)
rate = snapped;
The vision being something like that?
We'd also love for people to share what rates they commonly see used with timecode that aren't in the list we have so we can make sure the list reflects real workflows but is still acting as a reasonable guard against erroneous data.
We've been seeing various experimental material in a variety of rates but 90 is the highest we've had to deal with in stable production that's not on the current OTIO list. 120 is probably on the table with the future of immersive materials and that appears to be covered with @jminor's note about SMPTE 12-3.
After a discussion with the Technical Steering Committee - we felt that what we should do is:
- For
from_framesandfrom_timecode- theratearg should "snap" values that are nearly SMPTE rates (n / 1001) to the SMPTE ratefrom_timecodeshould have an optional arg to allow you to disable all snapping and validation on the rate ...
+1 for this approach, it's come up in parsing ALE files in https://github.com/AcademySoftwareFoundation/OpenTimelineIO/issues/1983
@mccartnm - yeah, the sample code you laid out is in the ballpark of what we were thinking.
Somewhat related, something that had occurred to me is that rather than having an NTSC rate "allow" list, we could consider using the generalized formula to NTSC-ify the rate. While it's tempting because it feel "future proof", I'm not sure if I like this - is 25.974 a framerate we really want to snap?
Python-ish pseudocode:
def rate_fraction_from_float(rate: float) -> Fraction:
# Whole numbers are easy
if isinstance(rate, int) or rate.is_integer():
return Fraction(rate)
whole_rate = math.ceil(rate)
ntsc_rate = whole_rate * 1000 / 1001
# The tolerance of 0.004 comes from 24000/1001 - 23.98
if abs(rate - ntsc_rate) < 0.004:
return ntsc_rate
return rate
I can see a case for the future proof version, since that's the underlying formula anyway. I'm alright with either path so long as we feel like it doesn't impede on the progression of frame rates that new vendors might come up with and require someone to revisit this code again.
Because of that, if the short list of rates is chosen, if we're able to set viable rates from another mechanism than just a hard-coded set (env, prefs/plugins, attr system similar to OpenImageIO::attribute, etc.) that might be beneficial too, while falling back to normal rates when not defined.