From dcc9eb722ebd485d2ed0e21c261b0a1b05497154 Mon Sep 17 00:00:00 2001 From: cubicibo <55701024+cubicibo@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:27:24 +0200 Subject: [PATCH] Add events garbage collection system and API. --- libass/Makefile_library.am | 6 ++-- libass/ass.c | 67 +++++++++++++++++++++++++++++++++++--- libass/ass.h | 20 +++++++++++- libass/ass_priv.h | 3 ++ libass/ass_render.c | 3 ++ 5 files changed, 91 insertions(+), 8 deletions(-) diff --git a/libass/Makefile_library.am b/libass/Makefile_library.am index 89cea7770..0e8dc1245 100644 --- a/libass/Makefile_library.am +++ b/libass/Makefile_library.am @@ -1,6 +1,6 @@ -LIBASS_LT_CURRENT = 12 -LIBASS_LT_REVISION = 1 -LIBASS_LT_AGE = 3 +LIBASS_LT_CURRENT = 13 +LIBASS_LT_REVISION = 0 +LIBASS_LT_AGE = 4 .asm.lo: $(nasm_verbose)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(top_srcdir)/ltnasm.sh $(AS) $(ASFLAGS) -I$(top_srcdir)/libass/ -Dprivate_prefix=ass -o $@ $< diff --git a/libass/ass.c b/libass/ass.c index 181c3dc58..7c884bb5c 100644 --- a/libass/ass.c +++ b/libass/ass.c @@ -190,14 +190,29 @@ static int test_and_set_read_order_bit(ASS_Track *track, int id) { if (resize_read_order_bitmap(track, id) < 0) return -1; - int index = id / 32; - uint32_t bit = 1u << (id % 32); + int index = id >> 5; + uint32_t bit = 1u << (id & 0x1F); if (track->parser_priv->read_order_bitmap[index] & bit) return 1; track->parser_priv->read_order_bitmap[index] |= bit; return 0; } +static inline void clear_read_order_bit(ASS_Track *track, int id) +{ + int index = id >> 5; + if (index < track->parser_priv->read_order_elems) { + uint32_t mask = ~(1u << (id & 0x1F)); + track->parser_priv->read_order_bitmap[index] &= mask; + } +} + +static inline void update_prune_ts(ASS_Track *track, const long long ts) +{ + if (ts < track->parser_priv->prune_next_ts) + track->parser_priv->prune_next_ts = ts; +} + // ============================================================================================== /** @@ -1015,8 +1030,10 @@ static int process_events_line(ASS_Track *track, char *str) event = track->events + eid; int ret = process_event_tail(track, event, str, 0); - if (!ret) + if (!ret) { + update_prune_ts(track, event->Start + event->Duration); return 0; + } // If something went wrong, discard the useless Event ass_free_event(track, eid); track->n_events--; @@ -1330,7 +1347,7 @@ void ass_process_chunk(ASS_Track *track, const char *data, int size, event->Start = timecode; event->Duration = duration; - + update_prune_ts(track, event->Start + event->Duration); goto cleanup; // dump_events(tid); } while (0); @@ -1359,6 +1376,46 @@ void ass_flush_events(ASS_Track *track) track->parser_priv->read_order_elems = 0; } +void ass_configure_prune(ASS_Track *track, long long delay) +{ + track->parser_priv->prune_delay = delay; +} + +void ass_prune_events(ASS_Track *track, long long deadline) +{ + if (deadline < track->parser_priv->prune_next_ts) + return; + + const bool check_readorder = track->parser_priv->check_readorder; + const int old_n_events = track->n_events; + + int n_kept = 0; + ASS_Event *events = track->events; + + track->parser_priv->prune_next_ts = LLONG_MAX; + for (int k = 0; k < old_n_events;) { + // discardable sequence + for (; k < old_n_events && events[k].Start + events[k].Duration < deadline; k++) { + if (check_readorder) + clear_read_order_bit(track, events[k].ReadOrder); + ass_free_event(track, k); + } + + // to-be-kept sequence + int move_from = k; + for (long long ts; k < old_n_events && (ts = events[k].Start + events[k].Duration) >= deadline; k++) + update_prune_ts(track, ts); + + // Relocate kept events + if (move_from < k) { + int cnt = k - move_from; + memmove(events + n_kept, events + move_from, cnt * sizeof(*track->events)); + n_kept += cnt; + } + } + track->n_events = n_kept; +} + #ifdef CONFIG_ICONV /** \brief recode buffer to utf-8 * constraint: codepage != 0 @@ -1725,6 +1782,8 @@ ASS_Track *ass_new_track(ASS_Library *library) if (!track->styles[def_sid].Name || !track->styles[def_sid].FontName) goto fail; track->parser_priv->check_readorder = 1; + track->parser_priv->prune_delay = -1; + track->parser_priv->prune_next_ts = LLONG_MAX; return track; fail: diff --git a/libass/ass.h b/libass/ass.h index ee297ea87..06a047b26 100644 --- a/libass/ass.h +++ b/libass/ass.h @@ -24,7 +24,7 @@ #include #include "ass_types.h" -#define LIBASS_VERSION 0x01703000 +#define LIBASS_VERSION 0x01703010 #ifdef __cplusplus extern "C" { @@ -734,6 +734,24 @@ void ass_process_chunk(ASS_Track *track, const char *data, int size, */ void ass_set_check_readorder(ASS_Track *track, int check_readorder); +/** + * \brief Prune events that preceed deadline. + * \param track track + * \param deadline cut-off timestamp in milliseconds. +*/ +void ass_prune_events(ASS_Track *track, long long deadline); + +/** + * \brief Configure automatic pruning of events. + * \param track track + * \param delay delay from "now" (ass_render_frame) in milliseconds. + * After every render, events whose undisplay timestamp predate + * "now - delay" will be deleted. A delay of 0 prunes aggressively. + * Negative delays disable automatic pruning. + * Disabled by default (no removal of events from memory). + */ +void ass_configure_prune(ASS_Track *track, long long delay); + /** * \brief Flush buffered events. * \param track track diff --git a/libass/ass_priv.h b/libass/ass_priv.h index e4f986959..ac6ec509b 100644 --- a/libass/ass_priv.h +++ b/libass/ass_priv.h @@ -65,6 +65,9 @@ struct parser_priv { uint32_t header_flags; uint32_t feature_flags; + + long long prune_delay; + long long prune_next_ts; }; #endif /* LIBASS_PRIV_H */ diff --git a/libass/ass_render.c b/libass/ass_render.c index d1bfad997..b92df1e76 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -3428,6 +3428,9 @@ ASS_Image *ass_render_frame(ASS_Renderer *priv, ASS_Track *track, ass_frame_unref(priv->prev_images_root); priv->prev_images_root = NULL; + if (track->parser_priv->prune_delay >= 0) + ass_prune_events(track, now - track->parser_priv->prune_delay); + return priv->images_root; }