Skip to content

Pause MediaPlayerElement when leaving the live tree - #11670

Open
Hemant Kumar (Hemantxk) wants to merge 5 commits into
mainfrom
user/Hemantxk/mpe-pause-play-experiment
Open

Pause MediaPlayerElement when leaving the live tree#11670
Hemant Kumar (Hemantxk) wants to merge 5 commits into
mainfrom
user/Hemantxk/mpe-pause-play-experiment

Conversation

@Hemantxk

@Hemantxk Hemant Kumar (Hemantxk) commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Fixes

Related work item: ADO Bug 40049143

PR Type

  • Bugfix
  • Feature
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no API changes)
  • Build related changes
  • Documentation content changes
  • Other (please describe):

Description

Current Behavior

When a Window containing a playing MediaPlayerElement is closed, its visual tree is torn down. If the app still retains the closed Window or MediaPlayerElement, the underlying MediaPlayer also remains alive and keeps playing. The video surface disappears with the Window, but its audio continues in the background.

New Behavior

When MediaPlayerElement leaves the live visual tree:

  • A playing or buffering MediaPlayer is paused.
  • Transient visual-tree resets are ignored unless the element's ContentRoot is shutting down.
  • The element records whether it successfully initiated the pause.
  • If the same element later re-enters the live tree, it resumes only when it initiated that pause.
  • Replacing the assigned MediaPlayer clears the pending resume state.
  • Resume failures are logged with LOG_IF_FAILED without failing tree entry.

Customer Impact

Closing or removing playback UI no longer leaves invisible media continuing in the background. If the same element is temporarily removed and later reattached, its previous playback continues automatically only when the element caused the pause.

There is no public API change.

Regression Potential

The change is limited to live-tree leave/re-entry handling. It pauses only a player that is currently playing or buffering, skips transient tree resets, and resumes only after a successful framework-initiated pause.

  • Low risk — isolated change, limited scope
  • Medium risk — touches shared components or public APIs
  • High risk — architectural or breaking API change

How Has This Been Tested?

Focused WPF TAEF tests:

  • PausesAndResumesMediaPlayerWhenRemovedFromTree — passed
  • PausesMediaPlayerWhenWindowCloses — passed

Result: 2 passed, 0 failed, 0 blocked.

Manual retained-Window validation confirmed that closing the playback Window stops its audio while the Window remains retained. Collapsing only the element's visibility continues playback, as expected, because it remains in the live tree.

This commit restores the behavior covered by those passing tests. The latest amd64fre Microsoft.ui.xaml.vcxproj build completed with 0 warnings and 0 errors. Per request, the tests were not rerun.

  • I have performed a self-review of my own code
  • I have added tests to cover my changes
  • Existing tests pass locally

Screenshots (if appropriate)

N/A — this is a playback lifecycle behavior change with no visual UI change.

Pause a playing MediaPlayer when its element leaves the live tree and resume only when that element initiated the pause. Cover remove/re-entry and retained Window-close lifecycles.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a3043399-4002-4d60-b928-b71920a36bf0
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the needs-triage Issue needs to be triaged by the area owners label Aug 27, 2026
Pause only when the element leaves because its ContentRoot is shutting down. Remove resume state and retain focused Window-close coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a3043399-4002-4d60-b928-b71920a36bf0
Keep a source token after the final event macro expansion without retaining the temporary undef directives.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a3043399-4002-4d60-b928-b71920a36bf0
Pause playing media on live-tree leave and resume when the same element re-enters after initiating that pause. Restore both focused lifecycle tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a3043399-4002-4d60-b928-b71920a36bf0
@Hemantxk
Hemant Kumar (Hemantxk) marked this pull request as ready for review August 27, 2026 13:22
@Hemantxk
Hemant Kumar (Hemantxk) requested a review from a team as a code owner August 27, 2026 13:22
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@Hemantxk

Copy link
Copy Markdown
Collaborator Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.

Preserve the original trailing blank line after the final event macro expansion.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a3043399-4002-4d60-b928-b71920a36bf0
@HariniMalothu17

Copy link
Copy Markdown

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.

IFC_RETURN(m_spTimedTextSource->SetMediaPlayer(nullptr));
}

bool shouldPauseMediaPlayer = bLive && !bVisualTreeBeingReset;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldPauseMediaPlayer = bLive && !bVisualTreeBeingReset means we pause on every ordinary removal from the live tree, not just on a window close - Frame navigation, ListView/ItemsRepeater container recycling, TabView content swap, re-parenting and Popup close all take this path. The linked bug is scoped to a terminal close, so we may have to consult IsShuttingDown() on both branches rather than treating !bVisualTreeBeingReset as sufficient. Please check whether the non-reset path should be pausing at all.


IFC_RETURN(__super::LeaveImpl(bLive, bSkipNameRegistration, bCoercedIsEnabled, bVisualTreeBeingReset));

if (shouldPauseMediaPlayer && m_spMediaPlayer && !m_resumeMediaPlayerOnEnter)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since e88e20cc removed m_bOwnsMediaPlayer there is no ownership signal left on the class, so this also pauses app-supplied players and players shared with a second MediaPlayerElement that is still live and on screen. SharedMediaPlayerSurvivesElementDestruction only pins the destruction path, so a shared player being paused by whichever element leaves first would not be caught. Please check whether we need a framework-created-player signal before mutating transport state here.

{
IFC_RETURN(UpdateAutoPlay());

if (m_resumeMediaPlayerOnEnter && m_spMediaPlayer)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_resumeMediaPlayerOnEnter is only cleared at line 267 for the MediaPlayer property and here at 706, so a Source change while the element is detached leaves it set and we force a Play() on the newly bound content on re-entry - that is the recycled container case for ListView and ItemsRepeater. It also bypasses AutoPlay="False", since UpdateAutoPlay() runs just before this, and it reverses a Pause() the app or user performed while we were out of the tree. Please check whether we should clear the flag in the Source case too, and re-read PlaybackState before resuming.

bool shouldPauseMediaPlayer = bLive && !bVisualTreeBeingReset;
if (bLive && bVisualTreeBeingReset)
{
if (CContentRoot* contentRoot = VisualTree::GetContentRootForElement(GetHandle()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If GetContentRootForElement returns null here we fall through with shouldPauseMediaPlayer still false from line 731, so the one branch that actually implements the fix quietly does nothing and the bug reproduces with no assert or trace to explain why. Please check whether treating an unresolved content root as shutting down would be the safer default on a path we already know is a tree reset.

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

Labels

needs-triage Issue needs to be triaged by the area owners

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants