Summary
On iOS, a live NextPVR stream eventually dies after entering Picture in Picture and then returning to full-screen playback.
The likely failure is that the NextPVR live-stream renewal/keep-alive stops running while the player is in PiP, or is not correctly restored when the player returns from PiP. NextPVR expires the live handle after roughly 15 seconds without a renewal and deletes the server-side timeshift buffer.
This appears related to #120, but the affected lifecycle here is iOS native PiP rather than ordinary pause/resume.
Steps to reproduce
- Use the NextPVR scheme on iOS with a NextPVR backend.
- Start a live TV stream.
- Enter native Picture in Picture.
- Leave the stream in PiP for long enough to exercise the keep-alive loop.
- Return to the player in full screen.
- Continue playback and wait.
Actual behavior
The stream eventually dies after returning to full screen. Playback may continue briefly from the local MPV buffer before reaching the point where the server-side stream was torn down.
The failure is not necessarily immediate on PiP restore, which suggests the server buffer may already have expired while PiP was active or that the renewal task/session is left in a bad state during the transition.
Expected behavior
The NextPVR live stream and its server-side timeshift buffer remain alive for the entire PiP session and after returning to full-screen playback. Playback should continue without requiring the user to stop and reopen the channel.
Relevant implementation
The keep-alive loop is started from PlayerView.onAppear and calls NextPVRClient.renewLiveStream() every 5 seconds. That method sends channel.transcode.lease and periodically fetches channel.stream.info.
The iOS onDisappear path deliberately avoids cleanup while native PiP is active:
let nativePiPActive = isUsingPixelBufferRenderer && (session.isPiPActive || session.dismissingForPiP)
if nativePiPActive {
// Don't stop player — mpv continues feeding PiP, so the server-side
// stream and its keepalive have to stay up too.
}
Native PiP is started by setting session.isPiPActive and session.dismissingForPiP, then calling appState.dismissPlayer().
Please verify that:
liveKeepaliveTask remains alive and continues sending renewals while PiP is active.
- Returning from PiP does not create a second player/session or orphan the original renewal task.
- The PiP delegate/lifecycle callbacks leave
session.isPiPActive and session.dismissingForPiP in the correct state.
- The active NextPVR SID and live-stream handle are unchanged across PiP entry and restoration.
- The keep-alive task is restarted or reattached if SwiftUI destroys and recreates
PlayerView during the transition.
Diagnostics requested
Capture the NextPVR DEBUG log and StreamClient network/player log while reproducing this. The important evidence is whether channel.transcode.lease requests continue during PiP and after full-screen restoration, and whether NextPVR reports:
Live stream expired without 15 second renewal.
StopStream(...)
Cleaning up: .../livetvbuffer/live-....ts
Acceptance criteria
- An iOS NextPVR live stream survives entering PiP, remaining in PiP, and returning to full screen.
- Renewals continue at a cadence safely below NextPVR's approximately 15-second expiry window throughout the transition.
- There is only one active renewal loop for the live stream.
- Returning from PiP does not stop or replace the active NextPVR stream unexpectedly.
- Add regression coverage for the PiP lifecycle/keep-alive ownership, including the case where
PlayerView disappears and is recreated while the native PiP session remains active.
Environment
- Device: <iPhone/iPad model>
- iOS:
- StreamClient build:
- NextPVR version:
Summary
On iOS, a live NextPVR stream eventually dies after entering Picture in Picture and then returning to full-screen playback.
The likely failure is that the NextPVR live-stream renewal/keep-alive stops running while the player is in PiP, or is not correctly restored when the player returns from PiP. NextPVR expires the live handle after roughly 15 seconds without a renewal and deletes the server-side timeshift buffer.
This appears related to #120, but the affected lifecycle here is iOS native PiP rather than ordinary pause/resume.
Steps to reproduce
Actual behavior
The stream eventually dies after returning to full screen. Playback may continue briefly from the local MPV buffer before reaching the point where the server-side stream was torn down.
The failure is not necessarily immediate on PiP restore, which suggests the server buffer may already have expired while PiP was active or that the renewal task/session is left in a bad state during the transition.
Expected behavior
The NextPVR live stream and its server-side timeshift buffer remain alive for the entire PiP session and after returning to full-screen playback. Playback should continue without requiring the user to stop and reopen the channel.
Relevant implementation
The keep-alive loop is started from
PlayerView.onAppearand callsNextPVRClient.renewLiveStream()every 5 seconds. That method sendschannel.transcode.leaseand periodically fetcheschannel.stream.info.The iOS
onDisappearpath deliberately avoids cleanup while native PiP is active:Native PiP is started by setting
session.isPiPActiveandsession.dismissingForPiP, then callingappState.dismissPlayer().Please verify that:
liveKeepaliveTaskremains alive and continues sending renewals while PiP is active.session.isPiPActiveandsession.dismissingForPiPin the correct state.PlayerViewduring the transition.Diagnostics requested
Capture the NextPVR DEBUG log and StreamClient network/player log while reproducing this. The important evidence is whether
channel.transcode.leaserequests continue during PiP and after full-screen restoration, and whether NextPVR reports:Acceptance criteria
PlayerViewdisappears and is recreated while the native PiP session remains active.Environment