Optimize carousel video preview switching: prime/pause/center-mode pipeline management#258
Draft
Optimize carousel video preview switching: prime/pause/center-mode pipeline management#258
Conversation
… unconditional element-setup, appsink drain Agent-Logs-Url: https://github.com/CoinOPS-Official/RetroFE/sessions/165a6dca-25d7-4c68-aa75-2fc21447ad71 Co-authored-by: inigomonyota <96964704+inigomonyota@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Optimize fullscreen crossfading previews with GStreamerVideo
Optimize carousel video preview switching: prime/pause/center-mode pipeline management
May 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fullscreen crossfade carousels (center playing, left/right primed-paused) suffered from slow first-frame latency and unnecessary audio decode on non-center slots. Pipeline teardown on every navigation prevented instant resume.
GStreamerVideo — new carousel APIs
prime(file, enableAudio=false)— prerolls a slot toGST_STATE_PAUSEDwith audio decode conditionally excluded. SetsGST_PLAY_FLAG_AUDIOon the playbin before the URI is applied so the decode chain is never built for side slots. Same session-ID / lifetime gating asplay().setCenterMode(bool)— manages center↔side audio transitions without pipeline teardown:true(side→center): enqueues flags update toVIDEO|AUDIO+ briefREADY→PAUSEDcycle to activate the audio chain. Handles theresume()-before-task race by checkingtargetState_ == Playingand continuing toGST_STATE_PLAYINGif needed.false(center→side): synchronously zerosAudioBusgain — no pipeline disruption.hibernate()—GST_STATE_READY+ full appsink drain + bus flush + audio mute, for slots no longer adjacent.GStreamerVideo — pipeline fixes
element-setupconnected unconditionally (was SW-decode path only).multiqueueuse-interleave=FALSEand queue caps now apply in HW accel mode too. Decoder-specific tweaks inside the callback remain conditional on!HardwareVideoAccel.GST_PLAY_FLAG_BUFFERINGandGST_PLAY_FLAG_TEXTnow explicitly cleared in all flag-setting paths.unload()appsink drain: both sinks drained afterGST_STATE_READYto prevent stale frames flashing on the nextplay().ScrollingList / VideoComponent wiring
notifyVideoCenterMode()iterates all slots and callsVideoComponent::setCenterMode(i == selectedOffsetIndex_)after both initialallocateSpritePoints()and everycomponents_.rotate()inscroll().VideoComponentpasses the call through to the underlyingGStreamerVideoviadynamic_cast.Original prompt
Repository: CoinOPS-Official/RetroFE (branch: current)
Goal
You are optimizing video preview switching behavior for fullscreen crossfading previews using multiple simultaneous GStreamerVideo instances (left/center/right). The UI shows a center fullscreen video playing; left and right videos are paused offscreen and should resume instantly when they become center. Crossfade is done visually (SDL textures) and audio should fade between videos.
Task
Investigate the existing implementation of GStreamerVideo (GStreamerVideo.cpp/.h) and the higher-level code that orchestrates preview video instances (likely a menu/view/carousel component). Then implement improvements for fastest first-frame and instant resume for neighbor videos while maintaining correctness and avoiding memory leaks.
Requirements
Deliverables
currentimplementing the changes.Notes
The following is the prior conversation context from the user's chat exploration (may be truncated):
User: anything i should be doing here to facilitate faster play/unload/play cycles when switching videos?
Assistant: Yes—your current design is already pointed in the right direction (keep a single
playbin3alive and “soft reset” between files), but there are a few concrete things you can do to make play → unload → play cycles faster and more deterministic.1) Prefer a single “reset to READY + flush + drain” path, not multiple teardown styles
Right now you have two different cleanup modes:
unload()keeps the pipeline and moves it toGST_STATE_READY(good for fast switching)stop()goes full teardown (GST_STATE_NULL, destroy watch source, unref pipeline, destroy textures, etc.) (slow)If “switch videos” is the hot path, make sure the UI uses
unload()(or a newresetForNextMedia()), and reservestop()only for final shutdown.Key detail: after
GST_STATE_READY, also drain both appsinks and clear your probe-side caches so you don’t carry stale samples into the next play.stop()viadetachAndDrainSink()unload()Recommendation: reuse
detachAndDrainSink()inunload()(without removing probes if you want), and always clearstagedSample_(you already do) plus drain appsink queues to avoid leftover preroll/sample being delivered immediately on next file.2) When switching files, set
pipelineto READY before setting a new URIFor fastest and cleanest transitions, the typical sequence is:
gst_element_set_state(pipeline, GST_STATE_READY)"uri"gst_element_set_state(pipeline, GST_STATE_PAUSED)(for preroll)PLAYINGwhen neededYou mostly do this across
unload()andplay(), but they’re separated and asynchronous, so you can end up with:play()arrivin...This pull request was created from Copilot chat.