summaryrefslogtreecommitdiffstats
path: root/sub/dec_sub.c
diff options
context:
space:
mode:
authorDudemanguy <random342@airmail.cc>2024-01-21 17:14:39 -0600
committerDudemanguy <random342@airmail.cc>2024-02-15 16:43:11 +0000
commit8f043de961f5b57c60e61e49b1841ad20faa64fe (patch)
tree69c4510a26174548181db3226328a06b0ba65a3a /sub/dec_sub.c
parent0a4b098c616bf1b6700bc88878b028bf1f446868 (diff)
downloadmpv-8f043de961f5b57c60e61e49b1841ad20faa64fe.tar.bz2
mpv-8f043de961f5b57c60e61e49b1841ad20faa64fe.tar.xz
player/sub: avoid wasteful subtitle redraws
This only affects two special cases: printing subtitles to the terminal and printing subtitles on a still picture. Previously, mpv was very dumb here and spammed this logic on every single loop. For terminal subtitles, this isn't as big of a deal, but for the image case this is pretty bad. The entire VO constantly redrew even when there was no need to which can be very expensive depending on user settings. Instead, let's rework sub_read_packets so that it also tells us whether or not the subtitle packets update in some way in addition to telling us whether or not to read more. Since we cache all packets thanks to the previous commit, we can leverage this information to make a guess whether or not the current subtitle packet is supposed to be visible on the screen. Because the redraw now only happens when it is needed, the mp_set_timeout_hack can be removed.
Diffstat (limited to 'sub/dec_sub.c')
-rw-r--r--sub/dec_sub.c61
1 files changed, 52 insertions, 9 deletions
diff --git a/sub/dec_sub.c b/sub/dec_sub.c
index 5221a65fde..c224a25fd6 100644
--- a/sub/dec_sub.c
+++ b/sub/dec_sub.c
@@ -20,6 +20,7 @@
#include <string.h>
#include <math.h>
#include <assert.h>
+#include <limits.h>
#include "demux/demux.h"
#include "sd.h"
@@ -62,6 +63,7 @@ struct dec_sub {
bool preload_attempted;
double video_fps;
double sub_speed;
+ bool sub_visible;
struct mp_codec_params *codec;
double start, end;
@@ -71,6 +73,7 @@ struct dec_sub {
struct demux_packet *new_segment;
struct demux_packet **cached_pkts;
+ int cached_pkt_pos;
int num_cached_pkts;
};
@@ -127,6 +130,7 @@ static void sub_destroy_cached_pkts(struct dec_sub *sub)
TA_FREEP(&sub->cached_pkts[index]);
++index;
}
+ sub->cached_pkt_pos = 0;
sub->num_cached_pkts = 0;
}
@@ -284,12 +288,47 @@ static bool is_new_segment(struct dec_sub *sub, struct demux_packet *p)
(p->start != sub->start || p->end != sub->end || p->codec != sub->codec);
}
-// Read packets from the demuxer stream passed to sub_create(). Return true if
-// enough packets were read, false if the player should wait until the demuxer
-// signals new packets available (and then should retry).
-bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force)
+static bool is_packet_visible(struct demux_packet *p, double video_pts)
{
- bool r = true;
+ return p && p->pts <= video_pts && (video_pts <= p->pts + p->sub_duration ||
+ p->sub_duration < 0);
+}
+
+static bool update_pkt_cache(struct dec_sub *sub, double video_pts)
+{
+ if (!sub->cached_pkts[sub->cached_pkt_pos])
+ return false;
+
+ struct demux_packet *pkt = sub->cached_pkts[sub->cached_pkt_pos];
+ struct demux_packet *next_pkt = sub->cached_pkt_pos + 1 < sub->num_cached_pkts ?
+ sub->cached_pkts[sub->cached_pkt_pos + 1] : NULL;
+ if (!pkt)
+ return false;
+
+ double pts = video_pts + sub->shared_opts->sub_delay[sub->order];
+ double next_pts = next_pkt ? next_pkt->pts : INT_MAX;
+ double end_pts = pkt->sub_duration >= 0 ? pkt->pts + pkt->sub_duration : INT_MAX;
+
+ if (next_pts < pts || end_pts < pts) {
+ if (sub->cached_pkt_pos + 1 < sub->num_cached_pkts) {
+ TA_FREEP(&sub->cached_pkts[sub->cached_pkt_pos]);
+ sub->cached_pkt_pos++;
+ }
+ if (next_pts < pts)
+ return true;
+ }
+
+ return false;
+}
+
+// Read packets from the demuxer stream passed to sub_create(). Signals if
+// enough packets were read and if the subtitle state updated in anyway. If
+// packets_read is false, the player should wait until the demuxer signals new
+// packets and retry.
+void sub_read_packets(struct dec_sub *sub, double video_pts, bool force,
+ bool *packets_read, bool *sub_updated)
+{
+ *packets_read = true;
mp_mutex_lock(&sub->lock);
video_pts = pts_to_subtitle(sub, video_pts);
while (1) {
@@ -320,8 +359,8 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force)
// happen for interleaved subtitle streams, which never return "wait"
// when reading, unless min_pts is set.
if (st <= 0) {
- r = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE &&
- sub->last_pkt_pts > video_pts);
+ *packets_read = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE &&
+ sub->last_pkt_pts > video_pts);
break;
}
@@ -341,8 +380,12 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force)
if (!(sub->preload_attempted && sub->sd->preload_ok))
sub->sd->driver->decode(sub->sd, pkt);
}
+ if (sub->cached_pkts && sub->num_cached_pkts) {
+ bool visible = is_packet_visible(sub->cached_pkts[sub->cached_pkt_pos], video_pts);
+ *sub_updated = update_pkt_cache(sub, video_pts) || sub->sub_visible != visible;
+ sub->sub_visible = visible;
+ }
mp_mutex_unlock(&sub->lock);
- return r;
}
// Redecode all cached packets if needed.
@@ -350,7 +393,7 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force)
void sub_redecode_cached_packets(struct dec_sub *sub)
{
mp_mutex_lock(&sub->lock);
- int index = 0;
+ int index = sub->cached_pkt_pos;
while (index < sub->num_cached_pkts) {
sub->sd->driver->decode(sub->sd, sub->cached_pkts[index]);
++index;