From fa89082f2ea8919997d99a040e23d4d4e346bb60 Mon Sep 17 00:00:00 2001 From: Dudemanguy Date: Sun, 26 May 2024 13:42:45 -0500 Subject: [PATCH] sub: avoid unneeded calls to is_animated 4e5d996c3a3f74a7308aee53614c2b94c71daebc added this as part of a series of patches written to avoid wasteful sub redraws when playing a still image with subs. The is_animated special case was specifically for ASS subtitles that have animations/effects and would need repeated redraws in the still image case. This check was done unconditionally for all ASS subtitles, but for very big ASS subtitles, this text parsing can get a bit expensive. Because this function call is only ever needed for the weird edge case of ASS subtitles over a still image, some additional logic can be added to avoid calling is_animated in the vast majority of cases. The animated field in demux_packet can be changed to a tristate instead where -1 indicates "unknown" (the default state). In update_subtitle, we can look at the current state of the video tracks and decide whether or not it is neccesary to perform is_animated and pass that knowledge to sd_ass before any subtitle packets are decoded and thus save us from doing this potentially expensive call. --- demux/packet.c | 1 + demux/packet.h | 2 +- player/sub.c | 14 ++++++++++---- sub/dec_sub.c | 2 +- sub/dec_sub.h | 1 + sub/sd_ass.c | 29 +++++++++++++++++++++++------ 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/demux/packet.c b/demux/packet.c index 93f64e844a..c78ad49026 100644 --- a/demux/packet.c +++ b/demux/packet.c @@ -64,6 +64,7 @@ static struct demux_packet *packet_create(void) .end = MP_NOPTS_VALUE, .stream = -1, .avpacket = av_packet_alloc(), + .animated = -1, }; MP_HANDLE_OOM(dp->avpacket); return dp; diff --git a/demux/packet.h b/demux/packet.h index 8087216355..6a0423fe8f 100644 --- a/demux/packet.h +++ b/demux/packet.h @@ -59,7 +59,7 @@ typedef struct demux_packet { double start, end; // set to non-NOPTS iff segmented is set // subtitles only - bool animated; + int animated; // -1 is unknown bool seen; int seen_pos; double sub_duration; diff --git a/player/sub.c b/player/sub.c index 65e5732e23..bdd19540b0 100644 --- a/player/sub.c +++ b/player/sub.c @@ -106,6 +106,15 @@ static bool update_subtitle(struct MPContext *mpctx, double video_pts, sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, ¶ms); } + // Checking if packets have special animations is relatively expensive. + // This is only needed if we are rendering ASS subtitles with no video + // being played. + bool still_image = mpctx->video_out && ((mpctx->video_status == STATUS_EOF && + mpctx->opts->subs_rend->sub_past_video_end) || + !mpctx->current_track[0][STREAM_VIDEO] || + mpctx->current_track[0][STREAM_VIDEO]->image); + sub_control(dec_sub, SD_CTRL_SET_ANIMATED_CHECK, &still_image); + if (track->demuxer->fully_read && sub_can_preload(dec_sub)) { // Assume fully_read implies no interleaved audio/video streams. // (Reading packets will change the demuxer position.) @@ -136,10 +145,7 @@ static bool update_subtitle(struct MPContext *mpctx, double video_pts, // Handle displaying subtitles on VO with no video being played. This is // quite different, because normally subtitles are redrawn on new video // frames, using the video frames' timestamps. - if (mpctx->video_out && mpctx->video_status == STATUS_EOF && - (mpctx->opts->subs_rend->sub_past_video_end || - !mpctx->current_track[0][STREAM_VIDEO] || - mpctx->current_track[0][STREAM_VIDEO]->image)) { + if (still_image) { if (osd_pts != video_pts) { osd_set_force_video_pts(mpctx->osd, video_pts); osd_query_and_reset_want_redraw(mpctx->osd); diff --git a/sub/dec_sub.c b/sub/dec_sub.c index c03393a007..ce51fdf8c8 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -319,7 +319,7 @@ static bool update_pkt_cache(struct dec_sub *sub, double video_pts) return true; } - if (pkt && pkt->animated) + if (pkt && pkt->animated == 1) return true; return false; diff --git a/sub/dec_sub.h b/sub/dec_sub.h index a40aa9bbfd..476e83a3f5 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -16,6 +16,7 @@ struct sd; enum sd_ctrl { SD_CTRL_SUB_STEP, + SD_CTRL_SET_ANIMATED_CHECK, SD_CTRL_SET_VIDEO_PARAMS, SD_CTRL_SET_VIDEO_DEF_FPS, SD_CTRL_UPDATE_OPTS, diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 82e39b6f54..e9f091682a 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -57,8 +57,9 @@ struct sd_ass_priv { struct mp_osd_res osd; struct seen_packet *seen_packets; int num_seen_packets; - bool *packets_animated; + int *packets_animated; int num_packets_animated; + bool check_animated; bool duration_unknown; }; @@ -351,19 +352,32 @@ static void filter_and_add(struct sd *sd, struct demux_packet *pkt) llrint(pkt->pts * 1000), llrint(pkt->duration * 1000)); - // This bookkeeping is only ever needed for ASS subs + // This bookkeeping only has any practical use for ASS subs + // over a VO with no video. if (!ctx->is_converted) { if (!pkt->seen) { for (int n = track->n_events - 1; n >= 0; n--) { - if (n + 1 == old_n_events || pkt->animated) + if (n + 1 == old_n_events || pkt->animated == 1) break; ASS_Event *event = &track->events[n]; - pkt->animated = (event->Effect && event->Effect[0]) || - is_animated(event->Text); + // Might as well mark pkt->animated here with effects if we can. + pkt->animated = (event->Effect && event->Effect[0]) ? 1 : -1; + if (ctx->check_animated && pkt->animated != 1) + pkt->animated = is_animated(event->Text); } MP_TARRAY_APPEND(ctx, ctx->packets_animated, ctx->num_packets_animated, pkt->animated); } else { - pkt->animated = ctx->packets_animated[pkt->seen_pos]; + if (ctx->check_animated && ctx->packets_animated[pkt->seen_pos] == -1) { + for (int n = track->n_events - 1; n >= 0; n--) { + if (n + 1 == old_n_events || pkt->animated == 1) + break; + ASS_Event *event = &track->events[n]; + ctx->packets_animated[pkt->seen_pos] = is_animated(event->Text); + pkt->animated = ctx->packets_animated[pkt->seen_pos]; + } + } else { + pkt->animated = ctx->packets_animated[pkt->seen_pos]; + } } } @@ -952,6 +966,9 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg) a[0] += res / 1000.0 + SUB_SEEK_OFFSET; return true; } + case SD_CTRL_SET_ANIMATED_CHECK: + ctx->check_animated = *(bool *)arg; + return CONTROL_OK; case SD_CTRL_SET_VIDEO_PARAMS: ctx->video_params = *(struct mp_image_params *)arg; return CONTROL_OK;