sub: avoid unneeded calls to is_animated

4e5d996c3a 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.
This commit is contained in:
Dudemanguy 2024-05-26 13:42:45 -05:00
parent 7e82108bec
commit fa89082f2e
6 changed files with 37 additions and 12 deletions

View File

@ -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;

View File

@ -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;

View File

@ -106,6 +106,15 @@ static bool update_subtitle(struct MPContext *mpctx, double video_pts,
sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, &params);
}
// 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);

View File

@ -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;

View File

@ -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,

View File

@ -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;