Skip to content

Commit 080ce70

Browse files
committed
fix rate truncation
1 parent d95c584 commit 080ce70

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

timecode/rate.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,15 +285,15 @@ func (r Rate) FrameDuration() time.Duration {
285285
if r.rateNum == 0 {
286286
return time.Nanosecond
287287
}
288-
return time.Second * time.Duration(r.rateDen) / time.Duration(r.rateNum)
288+
return time.Duration(1000000000 * float64(r.rateDen) / float64(r.rateNum))
289289
}
290290

291291
// Duration returns the duration of f frames at the edit rate.
292292
func (r Rate) Duration(f int64) time.Duration {
293293
if r.rateNum == 0 {
294294
return 0
295295
}
296-
d := time.Duration(f) * time.Second * time.Duration(r.rateDen) / time.Duration(r.rateNum)
296+
d := time.Duration(float64(f) * 1000000000 * float64(r.rateDen) / float64(r.rateNum))
297297
return r.Truncate(d, 2)
298298
}
299299

@@ -305,6 +305,18 @@ func (r Rate) Frames(d time.Duration) int64 {
305305
// Truncate clips duration d to the edit rate's interval length, while internally
306306
// rounding to precision digits.
307307
func (r Rate) Truncate(d time.Duration, precision int) time.Duration {
308+
i := int64(d)
309+
n := int64(r.FrameDuration())
310+
if x := i % n; x > n/2 {
311+
return time.Duration(i + n - x)
312+
} else if x > n/int64(precision) {
313+
return time.Duration(i - x)
314+
} else {
315+
return d
316+
}
317+
}
318+
319+
func (r Rate) TruncateFloat(d float64, precision int) time.Duration {
308320
pow := math.Pow(10, float64(precision))
309321
rd := r.FrameDuration()
310322
val := pow * float64(d) / float64(rd)

timecode/rate_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package timecode
1616

1717
import (
1818
"testing"
19+
"time"
1920
)
2021

2122
type RateTestcase struct {
@@ -39,6 +40,30 @@ var (
3940
}
4041
)
4142

43+
type RateDurationTestcase struct {
44+
Rate Rate
45+
}
46+
47+
var (
48+
RateDurationTestcases []Rate = []Rate{
49+
OneFpsRate,
50+
// IdentityRate,
51+
// IdentityRateDF,
52+
Rate23976,
53+
Rate24,
54+
Rate25,
55+
Rate30,
56+
Rate30DF,
57+
Rate48,
58+
Rate50,
59+
Rate60,
60+
Rate60DF,
61+
Rate96,
62+
Rate100,
63+
Rate120,
64+
}
65+
)
66+
4267
func TestTimecodeRateMin(t *testing.T) {
4368
for _, v := range RateMinMaxTestcases {
4469
a := NewRate(v.rateNumA, v.RateDenA)
@@ -72,3 +97,25 @@ func TestTimecodeRateMax(t *testing.T) {
7297
}
7398
}
7499
}
100+
101+
func TestTimecodeRateDuration(t *testing.T) {
102+
for i, v := range RateDurationTestcases {
103+
num, den := v.Fraction()
104+
real := time.Duration(float64(den) * 1000000000 / float64(num))
105+
if d := v.FrameDuration(); d != real {
106+
t.Errorf("[Case #%.2d] Wrong frame duration %d != %d (%d)", i, d, real, real-d)
107+
}
108+
}
109+
}
110+
111+
func TestTimecodeDuration(t *testing.T) {
112+
for i, v := range RateDurationTestcases {
113+
num, den := v.Fraction()
114+
for fps := v.Frames(time.Second) + 1; fps > 0; fps-- {
115+
real := time.Duration(float64(fps) * 1000000000 * float64(den) / float64(num))
116+
if d := v.Duration(fps); d != real {
117+
t.Errorf("[Case #%.2d] Wrong duration rate %s for %d frames %d != %d (%d)", i, v.RationalString(), fps, d, real, real-d)
118+
}
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)