sub: fix LRC lines with multiple timestamps

LRC subtitles can have lines with multiple timestamps, e.g.

[00:00.00][00:02.00]foo
[00:01.00]bar

Currently mpv shows only the "foo" that was decoded first, because it
compares the packet file position to check if a packet was already seen,
and it is the same for both occurrences of "foo". Fix this by also
comparing the pts.

This keeps comparing the packet position on top of the pts to not break
subtitle lines with the same timestamp, like:

1
00:00:00,000 --> 00:00:01,000
foo

2
00:00:00,000 --> 00:00:01,000
bar

where mpv shows both lines on top of each other. They are common in ASS
subtitles.

Fixes https://github.com/mpv-player/mpv/issues/13497.
This commit is contained in:
Guido Cella 2024-02-18 08:23:08 +01:00 committed by sfan5
parent a0447a370e
commit 748504de52
1 changed files with 15 additions and 8 deletions

View File

@ -55,13 +55,18 @@ struct sd_ass_priv {
struct mp_image_params video_params;
struct mp_image_params last_params;
struct mp_osd_res osd;
int64_t *seen_packets;
struct seen_packet *seen_packets;
int num_seen_packets;
bool *packets_animated;
int num_packets_animated;
bool duration_unknown;
};
struct seen_packet {
int64_t pos;
double pts;
};
static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts);
static void fill_plaintext(struct sd *sd, double pts);
@ -366,9 +371,9 @@ static void filter_and_add(struct sd *sd, struct demux_packet *pkt)
talloc_free(pkt);
}
// Test if the packet with the given file position (used as unique ID) was
// already consumed. Return false if the packet is new (and add it to the
// internal list), and return true if it was already seen.
// Test if the packet with the given file position and pts was already consumed.
// Return false if the packet is new (and add it to the internal list), and
// return true if it was already seen.
static bool check_packet_seen(struct sd *sd, struct demux_packet *packet)
{
struct sd_ass_priv *priv = sd->priv;
@ -376,19 +381,21 @@ static bool check_packet_seen(struct sd *sd, struct demux_packet *packet)
int b = priv->num_seen_packets;
while (a < b) {
int mid = a + (b - a) / 2;
int64_t val = priv->seen_packets[mid];
if (packet->pos == val) {
struct seen_packet *seen_packet = &priv->seen_packets[mid];
if (packet->pos == seen_packet->pos && packet->pts == seen_packet->pts) {
packet->seen_pos = mid;
return true;
}
if (packet->pos > val) {
if (packet->pos > seen_packet->pos ||
(packet->pos == seen_packet->pos && packet->pts > seen_packet->pts)) {
a = mid + 1;
} else {
b = mid;
}
}
packet->seen_pos = a;
MP_TARRAY_INSERT_AT(priv, priv->seen_packets, priv->num_seen_packets, a, packet->pos);
MP_TARRAY_INSERT_AT(priv, priv->seen_packets, priv->num_seen_packets, a,
(struct seen_packet){packet->pos, packet->pts});
return false;
}