Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea behind Timecode.frameRate ? #24

Closed
melMass opened this issue Jul 13, 2021 · 8 comments
Closed

Idea behind Timecode.frameRate ? #24

melMass opened this issue Jul 13, 2021 · 8 comments

Comments

@melMass
Copy link

melMass commented Jul 13, 2021

Hi,

I've read #4 but I'm not sure to understand why it was decided to use enums for frame rate or at least how to generate one from a double or a float?

Because doing this feels strange no?

func toFrameRate(from:Double)-> Timecode.FrameRate{
        switch from {
        case 24.0:
            return ._24
        case 25.0:
            return ._25
        case 60.0:
            return ._60
        default:
            return Timecode.FrameRate._30
        }
    }
@melMass melMass changed the title Idea behind Timecode.frameRate Idea behind Timecode.frameRate ? Jul 13, 2021
@orchetect
Copy link
Owner

orchetect commented Jul 13, 2021

Because doing this feels strange no?

It's strange because Timecode.FrameRate is a BITC timecode frame rate, and not a "video" frame rate. They are not actually the same thing. TimecodeKit is geared towards supporting DAW software that deals in established industry timecode frame rates.

In fact, DAW software relies on the user to tell it what timecode frame rate they are working in because it cannot always know for sure from a video file.

Video rates as a floating-point value are reductive and do not carry enough information to always accurately guess timecode frame rate. So there is no provision in the library to do it yet.

I'm working on an application that had the same problem as you - we're importing video files that only report a floating-point fps value and often lack any embedded meta-data to indicate what the actual timecode frame rate is suppose to be. We had to write a method that "guesses" the nearest timecode frame rate and it's not as simple as you might think.

In fact, this exact discussion has already come up if you checked the closed Issues:

#1

Ultimately the solution was a bit more complex than the sample code that was in that thread.

Perhaps TimecodeKit could use a guessFrameRate(fromVideoFloatingPointRate: Double) method. But it still wouldn't always guarantee an enum case that is accurate.

@orchetect
Copy link
Owner

Related: at one point, allowing arbitrary frame rate (ie: having an enum case like .video(Double)) was considered for instances where calculations could be done based on a frame rate but it would add complexity to the library and may not always have the desired outcome:

#4

@orchetect
Copy link
Owner

Perhaps there is a possibility of introducing a new struct called VideoTimecode which will allow separation of time domains. It could have methods to purely process floating-point video frame rates to/from timecode strings etc.

@melMass
Copy link
Author

melMass commented Jul 13, 2021

Thanks for the thoughtful explanation!
I had no idea about the precision issues, it seems logical for audio.
Using a custom function is actually going to work in my case.

@orchetect
Copy link
Owner

No worries, if you have any questions or ideas please feel free.

I know this is a common question/issue so it may either need a preface in the README.md or could become incorporated into the library as a feature if it makes sense.

@orchetect
Copy link
Owner

orchetect commented Jul 13, 2021

In short, there are times when a certain floating-point video frame rate could actually mean one of several different timecode frame rates. And then you can often get video frame rates that do not correspond to timecode frame rates at all (like variable frame rates in video codecs which, when read from a video's meta data, actually show the average frame rate. it gets weird).

It's easier to explain with examples, so maybe that could be put at the top in the README.md.

@orchetect
Copy link
Owner

orchetect commented Jul 30, 2021

Just an update:

I'm doing some investigation into video file metadata to see if there's a more reliable way to infer a BITC timecode frame rate from a video file. Because there are so many video container types and metadata is not consistently embedded, it's never a sure bet. But often there is more specialized information embedded than just a nominal/average floating-point fps.

Digging through AVFoundation and the FFmpeg source code has been useful. I might have a bit more time next week to get a parser method put together to scrape frame rate info and hopefully provide some useful functionality there.

@orchetect
Copy link
Owner

In release 1.2.7, a init?(raw: Double, favorDropFrame: Bool = false) init has been added to FrameRate.

It is a failable initializer that will return a valid FrameRate if the raw fps matches one of the supported BITC rates.

The exception is that certain rates can be interpreted as one of 2 or 3 different actual BITC rates. Drop frame information cannot be conveyed. For example, 29.97002997 is ambiguous as it is valid for both 29.97 non-drop, 29.97 drop, and 30 drop. For this purpose, the favorDropFrame: Bool = false parameter will return the drop-frame variant if the raw rate matches more than one rate.

So the utility of such an initializer is questionable, but there may be scenarios where heuristically inferring BITC rate is desired, perhaps as a springboard or default value that the user can then re-select at their discretion.

There is still much room for further research into video file metadata etc. that can be used to form frame rate information more accurately and deterministically. But for the time being, I will close this thread as that is a separate feature that may come at a later date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants