Skip to content

feat: isolate media player in internal pipeline with clocksync bridge#484

Merged
srperens merged 6 commits intomainfrom
feat/mediaplayer-internal-pipeline
Apr 10, 2026
Merged

feat: isolate media player in internal pipeline with clocksync bridge#484
srperens merged 6 commits intomainfrom
feat/mediaplayer-internal-pipeline

Conversation

@srperens
Copy link
Copy Markdown
Collaborator

Summary

  • Internal pipeline architecture: Media player now runs decoding/source in an isolated internal pipeline with clocksync pacing, bridged to the main pipeline via appsink→appsrc. This isolates downstream from seeks, file switches, and EOS.
  • Module split: Refactored the 1426-line monolith (mediaplayer.rs) into focused sub-modules: mod.rs, state.rs, bridge.rs, builder.rs, definition.rs.
  • Frontend improvements: Seek throttle (150ms), ±15s jump buttons, two-row button layout in both compact and full views, playlist editor with draggable split pane.
  • Cleanup: Removed standalone mediaplayer test harness binary and its docs. Restored unit tests with shared helper.
  • Bugfixes: Prevented panic on capless sample in bridge hot path; set_playlist now clamps current_index when the new playlist is shorter.

Test plan

  • cargo check passes
  • cargo clippy — no warnings
  • cargo test --test openapi_test — snapshot matches
  • cargo test --lib mediaplayer — 10/10 pass (including new test_set_playlist_clamps_index)
  • Manual: start flow with media player, verify playback, seek, next/prev, playlist edit
  • Manual: verify file switch produces no glitches downstream
  • Manual: verify seek during playback resyncs A/V correctly

🤖 Generated with Claude Code

Per Enstedt and others added 6 commits April 8, 2026 18:47
Refactor the media player block to use a separate internal GStreamer
pipeline behind an appsrc/appsink bridge, following the same pattern
as the WHIP ingest block. This fixes three problems:

1. No playback pacing: add clocksync(sync=true) so buffers flow at
   the correct rate when feeding a video compositor or live sink.
2. EOS propagation: EOS is now caught in the internal pipeline and
   triggers next-file advancement instead of stopping the main pipeline.
3. Seek disruption: seek operates only on the internal pipeline;
   downstream sees continuous timestamps via a computed offset.

Architecture:
  Internal pipeline: uridecodebin/urisourcebin -> clocksync -> appsink
  Main pipeline:     appsrc -> queue -> identity (video_out/audio_out)
  Bridge:            appsink callback -> push_sample with ts offset

The timestamp offset (shared AtomicI64 between audio/video) is computed
once from the first buffer and reset on seek, file switch, and resume
to prevent accumulated drift.

Also split mediaplayer.rs (1427 lines) into sub-modules following the
vision_mixer pattern: mod.rs, state.rs, builder.rs, bridge.rs,
definition.rs.

Frontend changes:
- Enable interactive seek bar in compact node view (click/drag)
- Enable seek slider in property inspector panel (show_full)
- Wire show_full into the properties panel for media_player blocks
- Add new "sync" block property (default true)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The interactive test harness (mediaplayer-test binary) and its docs
are outdated after the internal pipeline refactoring. The seek warning
in the API endpoint is no longer relevant since seek now operates on
the isolated internal pipeline without affecting downstream.

Removed:
- backend/src/bin/mediaplayer_test/ (1362 lines)
- docs/MEDIAPLAYER_TEST_HARNESS.md
- Cargo.toml [[bin]] entry for mediaplayer-test
- Seek warning log in api/mediaplayer.rs
- Outdated unit tests in mediaplayer/mod.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Re-add the unit tests that were removed with the test harness binary.
These test pure logic (URI normalization, registry, state, definition)
and remain valid after the internal pipeline refactoring.

Introduced a test_state() helper to reduce boilerplate in test setup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ity fixes

Add seek throttle (150ms) to avoid flooding the API during progress bar
drags. Add ±15s jump buttons flanking the seek bar in compact view and
in the full property inspector. Add stop button to both views.

Quality fixes:
- Fix registry leak: unregister media players from MEDIA_PLAYER_REGISTRY
  when flows stop
- Implement proper stop action (pause + seek to 0) instead of just pause
- Replace raw state strings with PlayerState enum throughout the stack
- Fix playlist/index race condition by combining both under a single
  RwLock<Playlist>
- Add seek bounds validation in the API endpoint
- Add switching_file flag to prevent spurious EOS during file switches
- Add debug logging for silent position query failures
- Sanitize error messages to not expose GStreamer internals to API clients
- Allow duplicate files in playlists
- Don't restart playback when saving a playlist on an already playing
  player
- Remove block_id label from property inspector

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Compact: remove stop button (too cramped), keep transport on row 1 and
jump buttons flanking the progress bar on row 2.

Right pane: transport buttons on row 1, jump buttons flanking the seek
slider on row 2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bridge's appsink callback called .unwrap() on sample caps, which
would crash the process if a sample arrived without caps during state
transitions. Now handles the None case gracefully.

set_playlist now resets current_index to 0 when the new playlist is
shorter than the current position, preventing out-of-bounds access
while the player is running.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@srperens srperens merged commit f1c676e into main Apr 10, 2026
7 checks passed
@srperens srperens deleted the feat/mediaplayer-internal-pipeline branch April 10, 2026 10:06
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.

1 participant