Skip to content

sub/sd_ass: subtitle timing rounding error - #18039

Merged
kasper93 merged 1 commit into
mpv-player:masterfrom
kako94321:fix#17453
May 31, 2026
Merged

sub/sd_ass: subtitle timing rounding error#18039
kasper93 merged 1 commit into
mpv-player:masterfrom
kako94321:fix#17453

Conversation

@kako94321

Copy link
Copy Markdown
Contributor

Use nearbyint() when the value is extremely close to an integer and floor() otherwise to prevent forward rounding caused by floating-point precision errors. This avoids early subtitle presentation on boundary frames.

Add test for ASS subtitle timing to ensure correct rendering around boundary timestamps.

Fixes: #17453

Read this before you submit this pull request:
https://github.com/mpv-player/mpv/blob/master/DOCS/contribute.md

Reading this link and following the rules will get your pull request reviewed
and merged faster. Nobody wants lazy pull requests.

Comment thread sub/sd_ass.c Outdated
@kasper93

Copy link
Copy Markdown
Member

@arch1t3cht: Could you do the same in aegi? floor(pts * 1000 + 1e-6); or something? I think it would be prudent to align on the solution. Adding epsilon fixes, the issue mentioned here #17453 (comment). (i.e. floor(518.81 * 1000 + 1e-6) = 518810)

@kasper93

Copy link
Copy Markdown
Member

Please squash the commits. I also think that a test for single math operation is a bit overkill.

@kako94321

Copy link
Copy Markdown
Contributor Author

Of course. I'm removing the test file before squashing then

@kako94321
kako94321 force-pushed the fix#17453 branch 3 times, most recently from ea85a60 to 05a9810 Compare May 29, 2026 22:37

@kasper93 kasper93 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@arch1t3cht

Copy link
Copy Markdown
Contributor

Could you do the same in aegi?

Yeah, I will. A hardcoded + 1e-6 does seem a bit hacky to me but there's no real better fully satisfying solution (except reworking the entire pipeline to never use floats lol) and I'm not sure how much it's worth bikeshedding this further.

@kasper93 kasper93 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact there are 3 other places where timestamp is converted. Could you update them too?

@cubicibo

Copy link
Copy Markdown

Why not just use nextafter(double, double) prior to flooring?

@kasper93

Copy link
Copy Markdown
Member

Why not just use nextafter(double, double) prior to flooring?

Having bigger epsilon is safer. If the pts accumulated more error across it's lifetime, it's not certain nextafter will be enough for this.

@kako94321

Copy link
Copy Markdown
Contributor Author

In fact there are 3 other places where timestamp is converted. Could you update them too?

Sure. Are you also referring to llrint(pkt->duration * 1000)?

@kasper93

Copy link
Copy Markdown
Member

In fact there are 3 other places where timestamp is converted. Could you update them too?

Sure. Are you also referring to llrint(pkt->duration * 1000)?

Yes, and 2 more. Search for llrint in that file.

Use floor(pts * 1000 + 1e-6) instead of llrint() for all timestamp
conversions to avoid floating-point precision errors causing early
subtitle presentation on boundry frames.

Fixes: mpv-player#17453
Signed-off-by: Ricardo Fonseca <ricardofonseca94321@tecnico.ulisboa.pt>
@kako94321

Copy link
Copy Markdown
Contributor Author

Done. Let me know if anything else needs changing.

Comment thread sub/sd_ass.c
@kasper93
kasper93 merged commit 74271a7 into mpv-player:master May 31, 2026
32 of 33 checks passed
arch1t3cht added a commit to TypesettingTools/Aegisub that referenced this pull request Jun 5, 2026
Coordinated with mpv-player/mpv#18039
The "integer millisecond count -> double -> / 1000 -> * 1000 -> floor -> int"
round trip is not always lossless, causing subtitle lines to not appear
when they should in rare edge cases.

For example, floor(518810 / 1000 * 1000) is 518809, causing a subtitle
line with start time 0:17:07.36 to not show up on its first frame when
that frame's timestamp is also exactly 0:17:07.36 (which matches frame
24632 (0-indexed) of a 24000/1001 video).

The proper solution here might be to get rid of the floating point
format for timestamps altogether and use integer millisecond values or
fractions, but that is a larger refactor. For now, adding a small
epsilon works as a workaround and matches mpv.

Fixes arch1t3cht#205.
Leinad4Mind pushed a commit to Leinad4Mind/Aegisub-1 that referenced this pull request Jul 28, 2026
Coordinated with mpv-player/mpv#18039
The "integer millisecond count -> double -> / 1000 -> * 1000 -> floor -> int"
round trip is not always lossless, causing subtitle lines to not appear
when they should in rare edge cases.

For example, floor(518810 / 1000 * 1000) is 518809, causing a subtitle
line with start time 0:17:07.36 to not show up on its first frame when
that frame's timestamp is also exactly 0:17:07.36 (which matches frame
24632 (0-indexed) of a 24000/1001 video).

The proper solution here might be to get rid of the floating point
format for timestamps altogether and use integer millisecond values or
fractions, but that is a larger refactor. For now, adding a small
epsilon works as a workaround and matches mpv.

Fixes arch1t3cht#205.
samgum pushed a commit to samgum/Aegisub that referenced this pull request Jul 29, 2026
Coordinated with mpv-player/mpv#18039
The "integer millisecond count -> double -> / 1000 -> * 1000 -> floor -> int"
round trip is not always lossless, causing subtitle lines to not appear
when they should in rare edge cases.

For example, floor(518810 / 1000 * 1000) is 518809, causing a subtitle
line with start time 0:17:07.36 to not show up on its first frame when
that frame's timestamp is also exactly 0:17:07.36 (which matches frame
24632 (0-indexed) of a 24000/1001 video).

The proper solution here might be to get rid of the floating point
format for timestamps altogether and use integer millisecond values or
fractions, but that is a larger refactor. For now, adding a small
epsilon works as a workaround and matches mpv.

Fixes arch1t3cht#205.
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

Successfully merging this pull request may close these issues.

.ass subtitle start at wrong frame

5 participants