Skip to content

A system interruption stops the renderer paths' clock and play() cannot start it again: the resume was written for AVPlayer #549

Description

@superuser404notfound

Field log from AetherPlayer 0.18.1 (69), iPhone 17 Pro, iOS 27.0 (24A435), engine 7.5.0.

A software-path session was playing when the viewer started AirPlay. The route switch arrived as an audio session interruption. The resume fired, and playback never came back: the master clock stopped and stayed stopped for the remaining two minutes of the log.

11:08:08.123  [AetherEngine] AVAudioSession interruption BEGAN reason=0 resumeArmed=true otherAudio=false silenceHint=false
11:08:08.134  [SWDiag] clk=10.81 dclk=1.00 aLead=4.00 vLead=0.32 parkedPkts=83 enq=+24 disp=+24
11:08:09.135  [SWDiag] clk=10.81 dclk=0.00 aLead=4.00 vLead=0.37 parkedPkts=82 enq=+1  disp=+1
11:08:18.360  [AetherEngine] AVAudioSession interruption ENDED shouldResume=true otherAudio=true resumeArmed=true autoResume=true
11:08:20.884  [SWDiag] clk=10.81 dclk=0.00 aLead=4.00 vLead=3.79 parkedPkts=0 enq=+0 disp=+0
   ...identical every second until the log ends at 11:09:00

autoResume=true is the engine stating that it called play(). clk never moved again, dclk stayed at 0.00, and the demux loop drained its parked packets and then stood still with 3.79 s of decoded video in hand.

Why

The interruption path and the resume it drives were written for the native path, where AVPlayer owns both the audio session and the clock and AVFoundation restores them. The renderer paths (SoftwarePlaybackHost, AudioPlaybackHost) own both themselves, and nothing on them recovers. Three gaps, all in the same direction:

  1. play() cannot start a clock the host did not stop. SoftwarePlaybackHost.play() re-rates the synchronizer only inside if pausedByHost (AudioPlaybackHost.play() is identical). A system interruption never goes through pause(), so the flag is false and the resume is a no-op on the clock by construction. The guard itself is right for a host pause (AU live tv #107: never rate-change an un-anchored synchronizer); what is missing is any other door.

  2. The audio session is activated once per load and never again. applyRendererAudioSession (AetherEngine+Loading.swift) is the only setActive(true) in the repo. After an interruption ends the app has to reactivate its own session; on these paths no one does. The log has exactly one renderer audio session active: line, at load, and none after the resume.

  3. The renderer's automatic flush is not observed. AVSampleBufferAudioRenderer flushes itself on an interruption and posts wasFlushedAutomaticallyNotification carrying the flush time, which is the documented signal to re-anchor and re-enqueue from there. There is no reference to it anywhere in Sources/. aLead=4.00 right up to the freeze and parkedPkts draining to zero afterwards is consistent with the queued lead having been thrown away.

There is also no AVAudioSession.routeChangeNotification observer in the engine at all, which is the other door the same event can arrive through.

Scope

Every renderer-path session on iOS, not only AirPlay: a phone call, Siri, or any other interruption should wedge the same way, and audio-only sessions carry the identical play() shape. The native path is unaffected, AVPlayer restores itself. tvOS sees interruptions far less often but the code path is shared.

Repro

No AirPlay needed: play a source that routes software (any forward-only HTTP source will do, or --sw), take a phone call, hang up. Expected: playback resumes. Observed shape: the clock never restarts and the picture freezes with the audio silent.

Reported on Discord (AirPlay half of the same report is filed separately).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions