Skip to content

fix: defer PiP visibility refresh on Document Picture-in-Picture enter#1868

Open
gparant wants to merge 1 commit intolivekit:mainfrom
workadventure:improve-picture-in-picture-detection-deferred
Open

fix: defer PiP visibility refresh on Document Picture-in-Picture enter#1868
gparant wants to merge 1 commit intolivekit:mainfrom
workadventure:improve-picture-in-picture-detection-deferred

Conversation

@gparant
Copy link
Copy Markdown

@gparant gparant commented Apr 2, 2026

WorkAdventure usage

WorkAdventure uses Document Picture-in-Picture via documentPictureInPicture.requestWindow(). After the PiP window is created, it appends a root container (meeting UI + <video> elements) to pipWindow.document.body, then updates app state.

You can test our use case here: WorkAdventure Virtual Office

Issue

For Document Picture-in-Picture, the enter event can be delivered before the host application has finished appending its DOM into documentPictureInPicture.window.document.

If onEnterPiP runs isElementInPiP immediately, pipWin.document.contains(video) may still be false, so adaptive-stream visibility is wrong.

Symptoms we saw: remote video appearing “stuck” or wrong adaptive-stream visibility after ~5s when the tab is hidden, and incorrect visibilityChanged on remote video tracks while Document PiP is active.

Reparenting can also leave the SDK’s observers / attachment state inconsistent until the track is re-attached.

Workaround in our app (until / in addition to SDK fixes): we detach and re-attach the RemoteVideoTrack to the same

// On mount: if PiP is already active, after tick() → detach + attach
if ($activePictureInPictureStore) {
    tick().then(() => refreshTrackAttachmentAfterPictureInPictureMove());
}
// On every transition of activePictureInPictureStore (open/close PiP), after tick() → detach + attach
const unsubscribePictureInPicture = activePictureInPictureStore.subscribe((inPip) => {
    if (inPip === pip) return;
    pip = inPip;
    tick().then(() => refreshTrackAttachmentAfterPictureInPictureMove());
});

Changes

In private onEnterPiP (RemoteVideoTrack.ts), defer isElementInPiP and handleVisibilityChanged using queueMicrotask + requestAnimationFrame, so embedders (including WorkAdventure) can finish mounting content in the PiP document before the SDK evaluates PiP visibility.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 2, 2026

🦋 Changeset detected

Latest commit: e832dec

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
livekit-client Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 2, 2026

CLA assistant check
All committers have signed the CLA.

…lity refresh

# Our usage
WorkAdventure opens Document PiP with documentPictureInPicture.requestWindow(), then appends a root div (meeting layout + <video> elements) to pipWindow.document.body. The enter listener in the SDK can run in the same turn before that append completes. Deferring the visibility refresh aligns with WorkAdventure’s flow: append the subtree into the PiP document, then update app state—so remote tracks attached to those <video> elements are evaluated only once they actually live under the PiP document.

# Issue
For Document Picture-in-Picture (documentPictureInPicture), the enter event can be delivered before the host application has finished appending its DOM (including <video> elements) into documentPictureInPicture.window.document.

In that case, onEnterPiP ran isElementInPiP immediately, while pipWin.document.contains(element) was still false, so the track was incorrectly treated as not in PiP and adaptive-stream visibility could be wrong.

# Change
In private onEnterPiP, defer the isElementInPiP update and handleVisibilityChanged to the next microtask and requestAnimationFrame, so embedder code can attach the subtree first and the check becomes reliable.
@gparant gparant force-pushed the improve-picture-in-picture-detection-deferred branch from 375ca13 to e832dec Compare April 2, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants