6.4.5 - Cold-start round trips on non-faststart MP4
Opening a non-faststart MP4 no longer pays a round trip for bytes the open already had, and one of the two remaining ones now overlaps the first request instead of queueing behind it.
Fixed
-
The return trip after a parse seek is a copy, not a third connection. Opening a non-faststart MP4 costs a data connection from byte zero, a seek to the trailing
moov, and a return to the first sample. The first two are the file layout. The third was self-inflicted:seekReconnectresets the window, so the seek to the tail discarded bytes that had been resident moments earlier, and the return re-fetched them.The open window is now parked across a parse seek (4 MB, cut from the window's start, which is where the demuxer comes back to) and serves that return as a copy. It is released once the demuxer reports its header pass done, through a new internal
AVIOProvider.markOpenPhaseFinished(). After that point a far seek is a scrub, whose landing zone the old window cannot serve, so keeping it would be pure cost. No additional bytes are fetched for this half. -
open()issues one speculative 64 KB suffix range, concurrent with the data connection.bytes=-nneeds no size, so it goes out before any response header exists. It covers what an open actually reads at the tail:mfraon fragmented MP4, and a trailingmoovwhose sample tables fit.A feature-length file's
moovruns into megabytes, does not fit, and still costs its own request. That is deliberate: fetching megabytes on a guess would compete with playback bytes on exactly the slow links this is meant to help. The parked window still removes the return trip there.Suffix ranges are not universally implemented, so the fetch runs on a delegate that decides at the response header rather than a completion handler that fires once the body has arrived. An origin answering 200 with the whole file is hung up on before its body, and only a 206 whose
Content-Rangeagrees with the received byte count is installed, so a proxy answering with a different region cannot place bytes at the wrong offset. Redirects follow the same credential policy as the other reader delegates.Measured on a 51 MB moov-at-end fixture: three sequential requests become two concurrent ones, with the
moovread and the return trip both gone. Reported with before/after traces by @digilearn-dev (#281). -
Display-criteria settle times in the log are measured, instead of having the Stage 1 budget added back in. Stage 2 reported
startGrace.ticks * 10 + stage2Ticks * 50, counting Stage 1's entire blind-poll budget whether or not it was spent, so a switch that settled one 50 ms tick after an immediately observed start logged~1050ms. Every settle time collected so far reads up to a second slow, including the ones the.briefplay-gate budget (#274) was reasoned about.The line now carries the real numbers plus how Stage 1 learned of the switch:
start pre-gate after 0ms, total 90ms, wherepre-gatemeans the panel was already switching when the gate opened andin-gatemeans it started inside it. The Stage 2 cap also stops attributing every unobservable switch to an unobservable DV panel.
Compatibility
No public API change. AVIOProvider is internal and its new member has a default implementation, so custom IOReader hosts are unaffected.
One behavioural change worth knowing about for hosts that count requests against an origin: a VOD open now issues one additional 64 KB request. It is cancelled at the response header on origins that do not implement suffix ranges.
Upgrading
.package(url: "https://github.com/superuser404notfound/AetherEngine", from: "6.4.5")Source-compatible.