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

Assignees

No one assigned

    Labels

    No labels
    No labels

    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