Skip to content

[GStreamer][WPE 2.38][WPE-2.46] HTMLMediaElement::suspend(BackForwardCache) should release hardware media resources on resource-constrained devices #1692

Description

@petartijanic01

When a page with an active <video > or <audio > element enters the Back/Forward Cache on navigation, HTMLMediaElement::suspend(ReasonForSuspension::BackForwardCache) only calls stopWithoutDestroyingMediaPlayer(). This pauses playback but keeps the full GStreamer pipeline (and its hardware decoder allocation) alive.
On embedded devices with limited hardware decoder slots (e.g., STBs with a single secure decoder), this prevents the newly navigated page from acquiring a decoder, resulting in a black screen or media playback failure.

But neither path ReasonForSuspension::PageWillBeSuspended releases the hardware decoder:

setBufferingPolicy(MakeResourcesPurgeable) — unimplemented in MediaPlayerPrivateGStreamer (empty virtual in MediaPlayerPrivate.h)
setPageIsSuspended(true) — only clears the hole-punch video rectangle, no pipeline state change

For comparison, Chromium destroys its media renderer on suspend (PipelineImpl::Suspend → DestroyRenderer).
The code contrast:

In Source/WebCore/html/HTMLMediaElement.cpp:

void HTMLMediaElement::stop()
{
    // ...
    stopWithoutDestroyingMediaPlayer();
    closeTaskQueues();
    clearMediaPlayer();  // <-- releases decoder, destroys pipeline
    // ...
}
void HTMLMediaElement::suspend(ReasonForSuspension reason)
{
    // ...
    case ReasonForSuspension::BackForwardCache:
        stopWithoutDestroyingMediaPlayer();  // <-- pauses but keeps decoder allocated
        setBufferingPolicy(BufferingPolicy::MakeResourcesPurgeable);  // no-op on GStreamer
        break;
    case ReasonForSuspension::PageWillBeSuspended:
        stopWithoutDestroyingMediaPlayer();  // <-- pauses but keeps decoder allocated
        setBufferingPolicy(BufferingPolicy::MakeResourcesPurgeable);  // no-op on GStreamer
        m_player->setPageIsSuspended(true);  // only hides video rect
        break;
    // ...
}

HTMLMediaElement::stop() properly releases all resources by calling clearMediaPlayer() after stopWithoutDestroyingMediaPlayer().

Reproduction scenario:

  1. Page A plays a video (hardware decoder acquired)
  2. JavaScript executes window.location.href = "pageB.html" (standard navigation)
  3. Page A enters BFCache — suspend(BackForwardCache) called — decoder NOT released
  4. Page B attempts to play video — no hardware decoder available — black screen or media playback failure

Proposed fix options:

  1. Call clearMediaPlayer() on BFCache entry — simplest fix. On restore, the media element would need to re-create the player and seek back. This mirrors what stop() already does, and is acceptable because BFCache restore is not guaranteed.
  2. Transition GStreamer pipeline to NULL state on BFCache entry — releases the decoder without destroying the player object. Lighter-weight, but requires re-negotiation of pipeline on restore.
  3. Override setBufferingPolicy() in MediaPlayerPrivateGStreamer — change pipeline to GST_STATE_NULL when MakeResourcesPurgeable is received. No changes to HTMLMediaElement.cpp needed since the call already exists in suspend(BackForwardCache). On resume, WebKit's existing reload path handles reconstruction

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions