]> granicus.if.org Git - libass/commitdiff
Don't rely on events being sorted in ass_step_sub()
authorwm4 <wm4@nowhere>
Mon, 7 Oct 2013 11:59:08 +0000 (13:59 +0200)
committerwm4 <wm4@nowhere>
Mon, 7 Oct 2013 15:13:50 +0000 (17:13 +0200)
ass_step_sub() assumed that the subtitle event list was sorted by event
start time, but that is not guaranteed. Making the list sorted is not
an option. (At least for now - too many issues are in the way to get
such a change being done.) Fix this function so that it works with an
unsorted event list.

Semantics regarding corner cases might be slightly different, such as
what happens if the now parameter coincides with event start/end, or
behavior with overlapping subtitles.

libass/ass.c

index f6fabdcb6b380a289d17f42400006d8ebba5ad05..19e1b5305a8944e14b333ac51b6a341bb5607105 100644 (file)
@@ -1242,33 +1242,45 @@ int ass_read_styles(ASS_Track *track, char *fname, char *codepage)
 long long ass_step_sub(ASS_Track *track, long long now, int movement)
 {
     int i;
+    ASS_Event *best = NULL;
+    long long target = now;
+    int direction = movement > 0 ? 1 : -1;
 
     if (movement == 0)
         return 0;
     if (track->n_events == 0)
         return 0;
 
-    if (movement < 0)
-        for (i = 0;
-             (i < track->n_events)
-             &&
-             ((long long) (track->events[i].Start +
-                           track->events[i].Duration) <= now); ++i) {
-    } else
-        for (i = track->n_events - 1;
-             (i >= 0) && ((long long) (track->events[i].Start) > now);
-             --i) {
+    while (movement) {
+        ASS_Event *closest = NULL;
+        long long closest_time = now;
+        for (i = 0; i < track->n_events; i++) {
+            if (direction < 0) {
+                long long end =
+                    track->events[i].Start + track->events[i].Duration;
+                if (end < target) {
+                    if (!closest || end > closest_time) {
+                        closest = &track->events[i];
+                        closest_time = end;
+                    }
+                }
+            } else {
+                long long start = track->events[i].Start;
+                if (start > target) {
+                    if (!closest || start < closest_time) {
+                        closest = &track->events[i];
+                        closest_time = start;
+                    }
+                }
+            }
         }
+        target = closest_time + direction;
+        movement -= direction;
+        if (closest)
+            best = closest;
+    }
 
-    // -1 and n_events are ok
-    assert(i >= -1);
-    assert(i <= track->n_events);
-    i += movement;
-    if (i < 0)
-        i = 0;
-    if (i >= track->n_events)
-        i = track->n_events - 1;
-    return ((long long) track->events[i].Start) - now;
+    return best ? best->Start - now : 0;
 }
 
 ASS_Track *ass_new_track(ASS_Library *library)