Fix FrameAtTime computation for CFR

The new formula is just the inverse function of the CFR part of
the TimeAtFrame function.

To see how the previous implementation was faulty, see either the added
tests, or
- In Aegisub, open a dummy video with a frame rate of 23.976
- Make a subtitle event with start time 04:44.41
- Double-click the line to (supposedly) seek to its first frame
- This will seek one frame earlier than it should, and the event will
  not be displayed on the resulting frame.
This commit is contained in:
arch1t3cht 2022-11-01 11:26:57 +01:00
parent a394aefd1a
commit 245cc68afa
2 changed files with 19 additions and 1 deletions

View file

@ -225,7 +225,7 @@ int Framerate::FrameAtTime(int ms, Time type) const {
return int((ms * numerator / denominator - 999) / 1000);
if (ms > timecodes.back())
return int((ms * numerator - last + denominator - 1) / denominator / 1000) + (int)timecodes.size() - 1;
return int((ms * numerator - numerator / 2 - last + numerator - 1) / denominator / 1000) + (int)timecodes.size() - 1;
return (int)distance(lower_bound(timecodes.rbegin(), timecodes.rend(), ms, std::greater<int>()), timecodes.rend()) - 1;
}

View file

@ -149,6 +149,12 @@ TEST(lagi_vfr, cfr_round_trip_exact) {
for (int i = -10; i < 11; i++) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i)));
}
ASSERT_NO_THROW(fps = Framerate(24000, 1001));
int frames[] = {-100, -10, -1, 0, 1, 10, 100, 6820};
for (int i : frames) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i)));
}
}
TEST(lagi_vfr, cfr_round_trip_start) {
@ -157,6 +163,12 @@ TEST(lagi_vfr, cfr_round_trip_start) {
for (int i = -10; i < 11; i++) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, START), START));
}
ASSERT_NO_THROW(fps = Framerate(24000, 1001));
int frames[] = {-100, -10, -1, 0, 1, 10, 100, 6820};
for (int i : frames) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, START), START));
}
}
TEST(lagi_vfr, cfr_round_trip_end) {
@ -165,6 +177,12 @@ TEST(lagi_vfr, cfr_round_trip_end) {
for (int i = -10; i < 11; i++) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, END), END));
}
ASSERT_NO_THROW(fps = Framerate(24000, 1001));
int frames[] = {-100, -10, -1, 0, 1, 10, 100, 6820};
for (int i : frames) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, END), END));
}
}
TEST(lagi_vfr, vfr_round_trip_exact) {