Skip to content

6.56.5 - An item's zero is where its playlist began

Choose a tag to compare

@superuser404notfound superuser404notfound released this 29 Aug 15:06
· 145 commits to main since this release

One report about a seek that was refused, and underneath it two things the refusal had been standing in front of. The last of them is not about rejoins at all: it is about which timeline a live item is even on.

A live item's zero is where its playlist began, not where the producer began (AE#446)

AVPlayer places a live playlist's content by the PLAYLIST it was handed, so an item's timeline starts at the first segment that playlist listed. For the item a session starts with, that is the producer's first segment and the two axes are the same one. For any item attached after the window has slid, which an in-place swap does routinely, it is not.

Three readings that had only ever been available separately, on one line:

#446 placement audit: item clock 70.01s in item range 0.00..42.00s, shift 1.40s
                      -> session 71.41s; the producer holds 51.40..111.40s

An item clock reading 70.01 while its own seekable range ends at 42.00 is not one timeline. The published playhead said 71.41 while the picture was at 121.4, and the rejoin to the place the viewer held aimed 50 s past it, onto the live edge, skipping the whole outage backlog. Every DVR leg the harness has ever run passed only because an 1800 s window does not slide inside a single run.

The offset is measured rather than assumed, and it verifies itself: one rule sizes both the playlist's first visible segment and the cache's eviction (LiveWindowSizing), so the producer's floor and the item's floor slide together and their difference holds still.

sample producer holds item reports difference
+1.5 s 51.40..116.40 0.00..42.00 50.00
+6.7 s 56.40..116.40 5.00..47.00 50.00
+13.0 s 66.40..126.40 15.00..57.00 50.00

It is latched per item, because it is a property of the playlist that item loaded, and re-measured when the item under the host changes. It reads 0 for the item a session starts with, so a live session that never swaps an item is unchanged by this release. Folded into the four conversions that cross the two axes: the published playhead, the live edge, a seek's landing, and the sampled edge a host scrub clamps against.

Harness verdict, live-only freeze leg with a 28 s outage, judged in segments:

before: consumed through seg13, resumed into seg18, 4 skipped, advanced 14.00s
        VERDICT: POSITION LOST
after:  consumed through seg13, resumed into seg11, 0 skipped,
        re-fetched 3 below (lookback allows 3), advanced 71.80s
        VERDICT: position held

The engine's own rejoin is not the host's scrubber (AE#446)

seek(to:) refuses a live seek on a session with no DVR window. That is defence-in-depth for a host that draws a scrubber it should have hidden, and it was reported by a client that keeps its rewind outside the engine and therefore loads with dvrWindowSeconds nil: the outage swap carried the place the viewer held, and the replay of that position was rejected before it could land.

The rejoin was never party to that contract. It picked its position out of content this same session cut and served. A seek now carries its origin and only a host scrub is refused. seekableLiveRange still reads nil on such a session, so nothing a host is told has moved.

The reporter's own suggestion, carrying the position in as the reload's start position, was not taken: LiveReloadPolicy.skipInitialSeek exists because of a device-verified wedge, where a start seek against a re-served backlog parked the reloaded item in waitingToPlay for the rest of the session.

A rejoin's landing is measured against what the producer holds (AE#446)

Lifting the refusal alone made things worse, which is worth stating because it is also why the reported case looked benign: the carried 71.40 s then landed at 43.40 s, four segments below the consumer's own last fetch.

At the moment a rejoin runs, neither edge the engine publishes is true. LiveWindow.edgeTime is a running maximum that an outage freezes BELOW the playhead that legitimately ran past it, and a freshly swapped item's seekableEnd is a range it has not finished reporting. The resident range is sampled at the seek instead (residentCeilingOutputSeconds is the other end of AE#441's floor), because the only thing that can disqualify a position the session itself served is eviction.

Added

  • NativeAVPlayerHost.seekableStart, the start of the item's seekable range. Only the end was ever mirrored, which follows a live edge and cannot answer whether a position is inside the item at all, which is the whole question a rejoin asks.
  • aetherctl live --live-only loads with no DVR window. Because live-only retention is a sliding 60 s, it is the only arm in which an item's own axis is observable at all.
  • A bounded placement audit after a swap, printing the item clock, the item's range, the shift, the offset and the producer's range together. That these were only ever available separately is why an item's clock and an item's range could disagree for a whole investigation without anyone being able to say so.

Thanks to @cmcpherson274, who flagged the refusal as an interaction rather than asserting it as a defect. Asserting it would have got the refusal lifted and nothing else, and the refusal was the least of it.


Full notes: CHANGELOG.md · 6.56.4...6.56.5