Skip to content

NextPVR: live TV stream dies after returning from iOS PiP to full screen #133

Description

@Drvolks

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

  1. Use the NextPVR scheme on iOS with a NextPVR backend.
  2. Start a live TV stream.
  3. Enter native Picture in Picture.
  4. Leave the stream in PiP for long enough to exercise the keep-alive loop.
  5. Return to the player in full screen.
  6. 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:

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions