package utilities import ( "fmt" "gopkg.in/yaml.v3" "math" "time" ) type Ratio struct { Numerator int Denominator int } func (r Ratio) Float64() float64 { return float64(r.Numerator) / float64(r.Denominator) } func (r Ratio) PTSToDuration(pts int64) time.Duration { return (time.Duration(pts) * time.Second * time.Duration(r.Numerator)) / time.Duration(r.Denominator) } func (r *Ratio) UnmarshalJSON(buf []byte) error { _, err := fmt.Sscanf(string(buf), "\"%d:%d\"", &r.Numerator, &r.Denominator) return err } func (r *Ratio) UnmarshalYAML(node *yaml.Node) error { _, err := fmt.Sscanf(node.Value, "%d:%d", &r.Numerator, &r.Denominator) return err } func (r Ratio) MarshalJSON() ([]byte, error) { return []byte("\"" + r.String() + "\""), nil } func (r Ratio) String() string { return fmt.Sprintf("%d:%d", r.Numerator, r.Denominator) } // Reciprocal get the reciprocal, for example, to convert frame rate into time base func (r Ratio) Reciprocal() Ratio { return Ratio{Numerator: r.Denominator, Denominator: r.Numerator} } const NTSCRatio = float64(1001) / 1000 const ratioEpsilon = 1.0e-9 func NewRatio(n, d int) Ratio { return Ratio{ Numerator: n, Denominator: d, } } // FPSToRatio Attempt common FPS conversions to ratios func FPSToRatio(fps float64) Ratio { // common NTSC ntscValue := fps * NTSCRatio // Number is whole NTSC if math.Abs(ntscValue-math.Round(ntscValue)) < ratioEpsilon { return NewRatio(int(math.Round(ntscValue))*1000, 1001) } // Number is whole other if math.Abs(fps-math.Round(fps)) < ratioEpsilon { return NewRatio(int(math.Round(fps)), 1) } // Get a few decimals of precision return Ratio{ Numerator: int(fps * 1000), Denominator: 1000, } }