Skip to content

AetherPlayer 0.17.0

Choose a tag to compare

@superuser404notfound superuser404notfound released this 05 Sep 10:43
· 166 commits to main since this release

Fifty-two AetherEngine releases in one step (6.34.1 to 6.68.0), and three of the things they added are now wired through to the app: a lip-sync offset you can set by ear, an honest answer when a file plays without sound, and a switch that lets a Mac composite Dolby Vision instead of throwing it away. The engine half of this release is mostly live streaming, which had a whole family of ways to lose its place, and two things that were quietly wrong rather than broken: a log that carried credentials, and a local server that answered the whole network.

Player

  • Lip-sync correction. J and K move the audio 50 ms later or earlier against the picture, the Audio menu carries the same two steps plus a reset and states the current offset, and the iOS tracks sheet gets a stepper. The value is kept across files and launches, because the error it corrects belongs to your soundbar, your AVR or your headphones and not to any one recording. It is applied to the session you are watching rather than at the next open: on the software path that lands inside a seek, on the loopback path through a session-preserving item replacement, measured at about 0.3 s of held picture with the position preserved to the sample. On a route where the engine never sees the timestamps (an AirPlay-direct or audio-only session) the value is kept for the next load instead of being silently ignored.
  • A file that plays without sound says why. A source whose audio no decoder in the build can open, or whose bridge could not be built, used to play video-only and silently, with the reason in the log and nowhere else. The engine now publishes that as a fact rather than a log line, so it is stated on screen once and the Stats inspector keeps the answer: a Delivery row saying whether the audio arrived as its authored bitstream, re-encoded through the bridge, decoded in software or not at all, and a Delay row carrying the lip-sync offset.
  • Dolby Vision composition, experimental. No Mac reports a Dolby Vision display, so a Profile 8.1 source is served as its HDR10 base layer and the per-frame metadata is discarded before it reaches the panel. A new Preferences switch serves it the way a Profile 5 source is served instead, and AVPlayer runs its own DV composition over it. Off by default, and it is a real experiment: the bitstream is untouched and only the container's claim about it changes, so on a display without the headroom expect a shifted picture, turn it back off and reopen the file.
  • A recents thumbnail stops stealing its neighbour's tap. On iOS, a thumbnail that is not 16:9 is scaled to fill and overhangs its cell. Clipping hides that overhang, it does not make it untouchable, so a tap near the edge of one cell opened the file next to it.

Playback fixes from the engine

  • The diagnostics log no longer carries credentials. Every line the engine emits is stripped of api_key / token / cookie values before it is written. This log exists to be attached to an issue, and a stream URL with a session token in its query was a live credential the moment it was shared.
  • The local playback server answers only its own session. The engine serves its remuxed stream over a loopback HTTP listener that binds 0.0.0.0, because an AirPlay receiver has to reach it over the LAN. There was no access control at all and the endpoint names were fixed, so the ephemeral port was the only thing anyone on the same WiFi had to find before they could pull what you were watching. Every path now carries a 128-bit per-session token and a request without it is refused before it reaches the router.
  • A live stream that goes quiet for a moment is no longer treated as one that ended. The window was sealed as a finished asset the instant the source missed its cadence, and an item that has read an ENDLIST never reloads its playlist again, so a source coming back could only be expressed as an item swap with a visible seam. Measured from a field report on a 1 s-segment stack: a 3.006 s stall closed a window that still had 14 s of runway in front of it, the source delivered again 0.6 s later, and the viewer paid 0.2 s of rebuffering the outage never required. The close now waits for three target durations of silence and cuts in early only when the runway ahead falls under two.
  • A DVR window deeper than 180 segments no longer freezes its own edge, and stops killing the source behind it. The window is sized in seconds, the producer refused to hold more than a fixed count of segments, and the playlist does not start sliding until the window's worth exists, so a deep window filled to that count and then parked for the rest of the session at exactly 180 times the segment duration. This app asks for a 30-minute DVR window, which on any normal cadence is well past that, so a live session that ran long enough hit it every time. A parked pump is also not reading, so the reader's runway filled at mux rate and single-connection sources backed up and died behind it, which had been read as a dead origin for three reporting campaigns. The window is sized by what the session can actually hold now, and advertises the depth that exists.
  • A live recovery lands where the session was, not at the live edge. A rejoin after an outage, and the item swap the engine makes on its own to recover, both used to hand the viewer the live edge, which on a DVR session means losing everything they had not caught up on yet. Related: an item's zero is now taken from the playlist that placed it rather than from where the producer started, so a rejoin's position means the same thing before and after.
  • Scrubbing a large local file costs less. Scrubbing back and forth across three resident 100 MB segments opened thirty software decoders in twenty seconds. The still extractor's decode context opens on VideoToolbox on that path now, falling back to software on failure and latching so it cannot retry per frame, and its cache holds six contexts instead of two, so a small scrub working set stays warm.
  • A 3D Blu-ray remux plays instead of showing black. These files carry both eyes inside one H.264 track, and VideoToolbox is handed whole samples with both views' NALs inside and renders nothing, so the session played its audio over a black screen. Routing reads the container's stereo declaration at load now and sends both-eyes-in-one-block carriage to the software decoder, which produces the left eye, the 2D fallback every other player shows.
  • An old AVI-in-Matroska remux keeps its picture order. A track carried the VFW way, which is how a repacked DivX / VC-1 rip arrives, has only decode timestamps. Generating presentation timestamps for it invented a display order and swapped every B/P pair, which reads as judder rather than as corruption, so it was easy to blame on the file.
  • Playback speed survives a resume. A session that rebuilt itself (a background return, an internal reload) came back at 1.0 and discarded the rate the viewer had set, with no way for a client to hold it from outside.
  • A tuner stream whose audio nothing can decode still opens promptly. An ATSC 3.0 channel carrying AC-4 spent the entire probe budget on an audio track that was never going to open. Also: an audio track's language is now declared where AVFoundation actually reads it, so an AirPlay receiver and any system UI name the track instead of showing "Not Specified".

Full engine notes: AetherEngine/CHANGELOG.md.

Full diff: v0.16.1...v0.17.0