Skip to content

v0.10.0 — lifecycle/seek/PiP rework and working Chromecast casting#55

Merged
omaralbeik merged 15 commits into
mainfrom
v0.10.0-dev
Jun 11, 2026
Merged

v0.10.0 — lifecycle/seek/PiP rework and working Chromecast casting#55
omaralbeik merged 15 commits into
mainfrom
v0.10.0-dev

Conversation

@omaralbeik

Copy link
Copy Markdown
Member

The v0.10.0 development line. Delivered as ordered milestones (M0→M5), each landed as its own reviewed commit, plus a final cast-hardening unit validated on physical hardware.

Milestones

  • M0 — test/CI hardening. Convert silent-pass tests to fail-red when behavior is absent (F036), dynamic-host fixture, tvOS-simulator CI job, and the race suites that previously self-skipped under CI=true (F041, F074, P1.13, P2.19).
  • M1 — event-delivery rework. One-shot terminal events on a no-longer-lossy stream (P2.14) with the first-subscriber reconciliation fix (F001) and riders (F004, F009, F012, F023, F037).
  • M2 — stop/teardown cluster. The stop/swap surfaces redefined as one unit (P1.10, P0.2, P0.3) with F003/F005/F022/F024/F034/F043/F044/F052. Includes the seamless set_media swap (no phantom .endReached).
  • M3 — seek & playback info. Seek semantics, playback-info surfaces, Volume 2.0, and the porting guide (P0.1, P3.1, F017, P0.4, P1.8, P0.5, P1.1).
  • M4 — PiP (spike-independent). Sample-buffer PiP half and riders (P1.11, P2.16, P1.12, F061).
  • M5 — device-validation harness. Matrix screens (a)–(g) + engine smoke screens, reading a gitignored operator-supplied streams.local.json.

Chromecast casting (validated on device)

Casting was unusable; this makes it work end-to-end, captured as scripts/patches/0001-chromecast-hardening.patch (applied by build-libvlc.sh by default):

  • Crash fix — a failed/slow receiver connection no longer aborts the host process (non-throwing connection path; Open() falls back to local playback).
  • SIGBUS fixDEMUX_GET_LENGTH passes the live flag the adaptive demuxer reads.
  • Subtitle burn-in — enable CC_ENABLE_SPU (upstream #if 0'd it); a selected subtitle forces transcode + soverlay blend onto the cast stream.
  • Player.recast carries audio/subtitle selection across the session restart (matches by language/name since ids are session-scoped), with unit tests for the matcher.
  • Showcase — cert + transcode-warning auto-accept, Local Network/Bonjour plist keys, subtitle picker, pullable device log path.

Verification

  • 1507 tests pass (CI=true swift test --no-parallel)
  • 0 strict-concurrency warnings
  • 100% public-symbol doc coverage
  • libvlc manifest check passes for all 8 slices (xcframework rebuilt with the chromecast patch)
  • Device-confirmed: cast video, subtitle burn-in, and seamless recast carry-over

Release note for the binary

Vendor/libvlc.xcframework is gitignored; the committed Package.swift references the released binary by url+checksum. Running release.sh 0.10.0 rebuilds/zips the xcframework, computes the checksum, and bumps Package.swift — that step carries the cast fix into the published binary.

🤖 Generated with Claude Code

omaralbeik and others added 13 commits June 10, 2026 16:22
PLAN.md is the authoritative spec for the v0.10.0 release: milestones
M0-M5, per-item API specs, test plans, and acceptance criteria.
CLAUDE.md records the working conventions for executing it (branch and
commit policy, build/test commands, device-only validation rules).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…namic-host fixture

F036 (#44): convert every silent-bailout poll guard (34 single-line + 11
multi-line) across the EventBridge, MediaListPlayerExtended, PiPController,
PlayerExtended, and MediaExtended suites to try #require; keep the three
guards whose else-blocks carry real fallback assertions. Revive the 16 dead
tests the conversion exposed: playback waits move off the no-output
TestInstance.shared onto makePlayback(), mute/volume event tests get the new
makeRealAudioPlayback() (the dummy aout swallows mute/volume reports), the
impossible .idle-after-stop wait now awaits .stopped, and two PiP playback
tests gain the canPlayMedia gate. Red-test verified: with event delivery
disabled, EventBridgeTests fails 11/14.

F041 (#45): add PlaybackFreeTeardownRaceTests — bridge attach/detach churn
across deinit, repeated native-handle swap with a live stream surviving
reattach, offloaded-deinit weak-probe drain, and deinit mid-consumption —
all CI-eligible so TSan/ASan finally see the stop/swap/deinit paths.
F074: drop the dead MemoryTests token from the sanitizer filter.

P1.13 (unit half): new tvos-test CI job running the suite on a tvOS
simulator; TEST_RUNNER_CI must be an environment variable, not a build
setting, for the forwarding to work. Encode two genuine tvOS-slice
behaviors the job surfaced (fresh-player pause-safety sentinel; renderer
discovery enumerable but unstartable).

P2.19 (CI half): per-arch libvlc.a member manifests + check script + CI job.

§3.14: Fixtures/DynamicHost proves the dynamic-intermediary topology ships
exactly one libvlc copy (iOS + tvOS sim builds, nm audit, launch check);
IntegrationTopology docc page records the supported contract. Two-package
layout required — a same-package dynamic product over statically-consumed
targets does not build under Xcode 26.

Evidence: strict-concurrency build 0 warnings; CI and local suites green
(1401 tests); TSan/ASan filters green; tvOS simulator suite green;
fixture verify.sh PASS both platforms.
Showcase iOS gains a Validation Harness section (§3.13 skeleton): operator-
supplied gitignored streams.local.json (bundled when present, Documents
fallback on device) with a committed example file; persisted PASS/FAIL/
observation recording with JSON export per matrix row; home screen listing
the full (a)-(g) + smoke-screen matrix with config-gated availability.

Screen (a) drives same-Player channel zaps under PiP with a timestamped
event log (P0.7 validate-first evidence). Screen (c) records the restore/X
baseline: a new @_spi(ValidationHarness) PiPController.nativeValidationProbe
surfaces the native window-controller class, AVPictureInPictureController
presence, delegate identity, and per-selector respondsToSelector results
needed by P0.6 spike steps 1-2.
…3, F037

P2.14: public EventBufferingPolicy (.newest(n) clamped >=1 / .unbounded),
Player.events(policy:filter:) with the parameterless accessor as sugar, and
a lossless lifecycle-only Player.stateTransitions stream. Subscriber filters
now evaluate outside the Broadcaster Mutex (re-entrant filters cannot
deadlock; libVLC's event thread never blocks under the lock), with a
single-subscriber fast path replacing the per-event snapshot allocation
(F012, folded per its conditional-rider rule). The internal observable
consumer rides an unbounded sourced subscription so Player.state can never
skip a transition under main-actor backlog; the events docs record the
swapped-handle non-delivery limitation and the blocking-filter teardown
caveat.

F001 (#34): the lifecycle phase machine was replaced outright — adversarial
review proved the single-critical-section fix still double-fired
onFirstSubscriber under schedule-order inversion. Attachment is now a
converging reconciliation loop on the serial queue, with passes scheduled
while the state lock is held so FIFO order matches transition order.

F004 (#36): BroadcasterBox holds the log broadcaster weakly, breaking the
self-contained cycle that leaked the log graph per non-shared VLCInstance
(weak-probe test, red-green verified).

F009: bridge fan-out gates each broadcaster on isEmpty, and the sourced
(internal) broadcast runs before the public one so user filters can only
delay public delivery.

F037 (#46): the public stream survives the native-handle swap and yields
events sourced from the replacement handle (red-green verified against a
disabled reattach). F023: all three Showcase diagnostics case studies
subscribe before play() and demonstrate .unbounded + subscription filters.

Tests: Broadcaster policy/re-entrancy/churn battery; headless pinning tests
through a new EventBridge._broadcastForTesting hook (internal-consumer
unboundedness, policy+filter plumbing, stateTransitions ordering); playback
backlog/filter/stateTransitions integration tests. Evidence: strict build 0
warnings; 1419 tests green (CI and local); TSan and ASan filters green.
…2, F024, F034, F043, F044, F052

P1.10: stopAndWait() awaits the explicit stop's terminal event on a
pre-subscribed unbounded sourced stream (terminal-state double-check closes
the missed-event stall; observable mirror reconciled on return; 10 s
defensive ceiling) and shutdown() is the awaitable deinit choreography —
one shared teardownNativePlayer for both paths, list-player detach, intent
reset, and an inert replacement handle so post-shutdown calls are no-ops.

P0.2: PlaybackEndCoordinator decides on libVLC's event thread whether a
stopped transition is a natural end; the callback synthesizes .endReached
immediately after the .stopped broadcast with the same source. stop()
records the library stop unless the native state is already terminal; the
handle-replacement path clears pending causes (the old handle's Stopped is
unobservable); plain load() neither clears nor marks — empirically, the
pinned libVLC 4 replaces started media seamlessly (mediaStopping →
mediaChanged, no Stopped), proven by red-green test. didReachEnd mirrors
the event, latching only while playback intent is inactive. MediaListPlayer
attachment suppresses synthesis; detach marks the deferred native stop
before lifting suppression, and deinit lifts it too (weak back-references
read nil during deinit — the ownership guard accepts that arm).

P0.3: recast(to:) switches renderers mid-session over the lazy handle
replacement — carries per-player state, restores prior renderer state only
when the throw precedes the handle commit, awaits new-session playing then
seekability before restoring the captured position, and never awaits the
old handle's stop. tvOS renderer impotence documented on every renderer
API (P2.19 doc rider).

F003: carryOverPerPlayerState re-applies marquee (text from the shadow,
never the mid-bust live value), logo, adjustments, stereo/mix, teletext,
deinterlace, audio output routing, and the accumulated viewpoint across
the swap; A-B loop, track/chapter/title, and program selection are
documented deliberate resets. F005: the list player re-binds its native
handle on every replacement. F022: Logo reads the live pointer. F024: all
three Showcase playlist analogs drain the Player through stopAndWait().
F034 (#43): setRate is internal; setPlaybackRate is the public mutator.
F043/F044 (#48): deterministic weak-probe drawable-release tests. F052:
the cleanup-timing probe measures awaited shutdown() completion.

Adversarial review (22 confirmed findings) hardened the cluster: phantom
endReached classes closed with red-green regression tests, coordinator
decision-table unit suite (CI-eligible), deinterlace carry-over native
read-back parity, stop() TOCTOU documented. The full end-reached matrix
passed a one-off tvOS-simulator playback run; CI-gating deviation for
PLAN §3.3's "green on CI" wording recorded in PROGRESS.md.

Evidence: strict-concurrency build 0 warnings; 1457 tests green (CI mode
and two local playback runs); TSan and ASan filters green; lint clean
(Player.swift split into Player+Teardown/Player+Drawable, PiP probe into
PiPController+Validation to satisfy file_length).
…1, Volume 2.0, porting guide

P0.1: lenient seek for live and unknown-duration media — seek(toPosition:
fast:) over set_position and jump(by:) over jump_time, both Bool-returning
and throw-free, never deriving targets from currentTime/duration. The
pinned binary reports success even with no media, so a session gate fronts
both: playback intent (set synchronously by play()) or a non-terminal
native state — the mirror-state variant deterministically no-op'd the
play-then-seek-to-live-edge pattern and was fixed under review with
same-turn tests. P3.1: fast: threaded through both strict call sites,
discriminating coverage on a new committed sparse-keyframe fixture
(I-frames at 0 s/10 s; fast snaps, precise lands — red-green verified).
F017: every seek path publishes derived position (and currentTime) when
computable, including paused jumps.

P0.4/P1.8: videoSize, hasVideoOutput, and stored activeVideoOutputs.
Empirical findings against the pinned binary baked into the design:
has_vout always returns 1 (pre-created window vout) and
libvlc_video_get_size probes the selected track, not a vout — so
hasVideoOutput is videoSize != nil with the mechanism documented.
Invalidation rides .voutChanged and .tracksChanged (adaptive switches emit
no size event); the count resets on media changes and on the handle swap,
where the old handle's voutChanged(0) is source-filtered away.

P0.5: SubtitleScale(approximatePoints:basePoints:) with the no-live-
absolute-change limitation documented and the freetype-fontsize escape
hatch marked experimental pending device validation.

P1.1: VLCInstance init widened in place with applicationName/httpUserAgent
(nil defaults byte-identical), plus setUserAgent/setAppID. Wire test
against a local socket server proves the custom UA reaches the HTTP
request (libVLC appends its product token) and the default carries
SwiftVLC.

Volume ceiling widened to 0.0...2.0 (200%) across the type, docs, tests,
and all six Showcase volume surfaces; native wire-through asserts 200.
VLCKit porting guide ships with nine idiom sections, every claim
symbol-linked; docc --analyze is at zero warnings after disambiguating
the enum-case/accessor link collisions. Also repairs the iOS Showcase
build broken by the M2 file split (nativeBackend visibility).

Evidence: strict-concurrency build 0 warnings; 1489 tests green (CI mode
and consecutive local playback runs); TSan/ASan green; lint and format
clean; docc link analysis clean.
… half, F061

P1.11/P2.16: PiPVideoView gains startsAutomaticallyFromInline and
managesAudioSession (defaults preserve behavior; inert-but-accepted on
macOS, documented). The auto-PiP flag reaches both backend sites — the
native drawable protocol method no longer returns a literal true, and the
sample-buffer path sets the AVKit flag. With managesAudioSession false the
library never touches the shared session; with it true the category is set
at init and setActive(true) is a one-shot deferred to start(), the first
active playback intent, a willStart from an AVKit-initiated auto-PiP, or
an already-active-at-construction seed (the intent stream carries
transitions only — review-found hole).

P1.12 (sample-buffer half, unconditional): PiPStopReason/PiPEvent and the
Broadcaster-backed pipEvents stream. The delegate emits willStart/didStart
and willStop/didStop with reasons; failedToStart now carries the
previously-discarded error. The upstream restore hook makes restore-vs-
close discriminable on this path today: the restore callback records
.restoreRequested before invoking onRestoreUserInterface, programmatic
stop() records .unknown unconditionally (a stop in the start-animation
window would otherwise read as the user's close tap), and a natural end
maps to .mediaEnded — first-wins precedence documented and tested. The
native path synthesizes didStart/didStop(.unknown) from its active flips;
will/failed events and richer reasons there await the interception spike,
as documented. Handle replacement (recast) tears PiP down with
didStop(.unknown), stated on both APIs.

F061: the @objc drawable selectors invoked from libVLC's vout thread are
nonisolated with immutable-after-init reads and documented off-main
contracts, so future mutable access fails to compile.

The native-path restore interception (P0.6 spike steps 3-5) and the P0.7
continuity decision remain device-gated; harness screens (a)/(c) carry the
probes. Evidence: strict build 0 warnings; 1502 tests green (CI and local);
TSan/ASan green; iOS/macOS Showcase schemes and the tvOS test slice build;
review findings (activation seed, stop-reason window) fixed.
…gine smoke screens

The Showcase iOS Validation Harness now covers the full §3.13 matrix:
(b) auto-PiP trigger conditions driving the new startsAutomaticallyFromInline
knob (init-time, view rebuilt via .id), (d)/(d') renderer discovery plus
live recast(to:) hand-off with continuity logging, (e) background audio
continuation without PiP — the release-gating §3.15 contract — with
once-per-second wall-clock timeChanged sampling so continuity is verifiable
after foregrounding, (f) lenient seek probes against a timeshift stream
with live Bool/throw readouts, and (g) the --freetype-fontsize escape
hatch on a dedicated instance with a SubtitleScale comparison toggle. One
parameterized engine smoke screen instantiates for live TS, HLS live, VOD,
and catch-up: start latency, decoded size/output count, tracks and codecs,
statistics, and seek probes. Every screen records PASS/FAIL/observation
through the persisted recorder; rows stay config-gated with explanations;
HarnessHome is launchable via -UITestRoute HarnessHome.

Validated in simulators with an operator-style gitignored config: the iOS
app launches, reports the bundled config and its missing keys, gates rows
exactly as specced, and plays remote streams with the lossless filtered
event stream live; the tvOS app launches and plays the same case study.
PiP rendering is simulator-blind by design — all PiP-visual acceptance
lives in PROGRESS.md's PENDING-DEVICE ledger with step-by-step
instructions.

Evidence: CI suite green (1502 tests / 121 suites); lint and format clean;
iOS and tvOS Showcase schemes build, install, launch, and play in their
simulators.
A full-branch hygiene review (every finding adversarially verified)
confirmed 30 defects, all addressed:

Dead code: unused imports dropped (os in Player.swift, Foundation in
Player+Drawable and PiPController+Events, AVKit/Foundation in
PiPController+Validation, AVFoundation in PiPController+Testing);
EventBridge's end coordinator de-optionalized end-to-end with its
unreachable guard removed; never-exercised parameter defaults deleted
(EventBridge stream factories, the single-caller timeout parameters in
awaitTerminalStop/awaitPlaying/awaitSeekability, now inlined);
PiPController+Validation narrowed to its only producing platform.

Stale comments: the close-button delegate-routing doc now accounts for
the will-stop callback; the .unknown stop-reason doc no longer claims a
case the resolver never produces; the activeVideoOutputs cross-reference
no longer equates the stored vout count with the selected-track probe;
the coordinator doc attributes its writers correctly; the
vendor-manifest workflow comment describes what setup-dev.sh actually
does; the shutdown-timing test is named for what it measures.

Narration: every "historical default", "widened this release",
"predates", "former ceiling", and past-tense blind-spot comment is
rewritten to the standing contract it was gesturing at.

Scaffolding: the undocumented lifetime-extension assignment is explicit;
the HarnessHome route carries its documented launch instruction; and
Package.swift returns to the pinned v0.9.1 url+checksum binaryTarget —
the committed local-path form was dev-mode working state that left a
fresh clone unresolvable and hollowed the CI cache keys; setup-dev.sh
applies that flip uncommitted.

Evidence: strict-concurrency build 0 warnings; 1502 tests green in CI
mode and a full local playback run; TSan and ASan filters green; lint
and format clean; docc analysis at zero warnings; the iOS Showcase
scheme builds against the pinned manifest.
CLAUDE.md, PLAN.md, and PROGRESS.md are session-working artifacts — the
implementation plan, its execution guide, and the running audit trail —
not part of the library. They stay on disk for local work and are now
gitignored.
Casting was unusable: connecting to an unreachable or slow receiver
aborted the host process, and even a successful cast crashed or showed
no subtitles. This reworks the libVLC chromecast module (captured as
scripts/patches/0001-chromecast-hardening.patch, applied by
build-libvlc.sh by default) and the Player/Showcase surfaces around it.

libVLC chromecast module:
- Make the control connection non-throwing. A failed TLS connect left
  the receiver path throwing std::runtime_error across the cast thread /
  sout boundary, terminating the app; it now leaves the object
  disconnected and Open() fails cleanly to local playback.
- Pass the live flag to DEMUX_GET_LENGTH. The adaptive demuxer reads two
  va_args; the one-arg call wrote through a stray stack slot (SIGBUS)
  once a cast reached adaptive content.
- Enable CC_ENABLE_SPU. Upstream gates subtitle support off behind
  #if 0; a selected subtitle now forces a video transcode and blends in
  (soverlay), burning subtitles into the cast stream.

Player.recast:
- Carry the audio and subtitle selection across the session restart.
  Track ids are session-scoped, so matchingTrack() falls back to
  language then name; an unmatched track stays at the new default.

Showcase (manual cast validation):
- CastTrustResponder accepts the receiver's self-signed TLS certificate
  and the transcode performance warning so a real cast can proceed.
- Info.plist gains the Local Network / Bonjour keys renderer discovery
  needs on-device.
- MatrixScreenD gets a subtitle picker; the log mirror resolves a
  relative path under Documents so device logs can be pulled back.

fix-duplicate-symbols.sh localizes the pure-C ytdl copy of the duplicate
json symbols instead of the chromecast object, leaving the cast path's
C++ unwind tables untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 11, 2026 01:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR advances SwiftVLC to the v0.10.0 development line by reworking lifecycle/event delivery semantics (including deterministic natural-end signaling), adding mid-playback renderer “recast” support (Chromecast-focused), expanding PiP controls/telemetry, and hardening CI/fixtures to validate these behaviors across platforms.

Changes:

  • Introduces lossless (policy-based) event-stream buffering, a deterministic .endReached signal synthesized from libVLC’s .stopped, plus new teardown and race-stress coverage.
  • Adds mid-playback renderer switching via Player.recast(to:), video-output introspection (videoSize / hasVideoOutput), and improves state carry-over across native-handle replacement.
  • Adds a device-validation harness (streams.local.json-driven), updates Showcase/UI routes, and adds CI gates for tvOS runtime tests and vendored libVLC slice-manifest drift.

Reviewed changes

Copilot reviewed 114 out of 124 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Tests/SwiftVLCTests/Support/TestMedia.swift Adds sparse GOP fixture URL for seek testing.
Tests/SwiftVLCTests/Support/TestInstance.swift Adds “real audio output” test instance factory.
Tests/SwiftVLCTests/Playlist/MediaListPlayerExtendedTests.swift Uses per-test playback instances + stricter #require polling.
Tests/SwiftVLCTests/Player/PlayerVideoInfoTests.swift New tests for videoSize/hasVideoOutput/activeVideoOutputs.
Tests/SwiftVLCTests/Player/PlayerStopTeardownTests.swift New tests for stopAndWait() and shutdown() teardown guarantees.
Tests/SwiftVLCTests/Player/PlayerRecastTrackMatchTests.swift New tests for track matching across recast sessions.
Tests/SwiftVLCTests/Player/PlayerExtendedTests.swift Updates volume ceiling test and polling behavior.
Tests/SwiftVLCTests/Player/PlayerEventTests.swift Updates expected event set to include .endReached.
Tests/SwiftVLCTests/Player/PlayerEventHandlerTests.swift Adjusts platform expectations and event handler coverage.
Tests/SwiftVLCTests/Player/PlayerEventDescriptionTests.swift Adds .endReached description expectation.
Tests/SwiftVLCTests/Player/PlayerEventAccessorTests.swift Adds .endReached accessor coverage.
Tests/SwiftVLCTests/Player/PlaybackValuesTests.swift Updates Volume ceiling to 2.0 + adds SubtitleScale(approximatePoints:) tests.
Tests/SwiftVLCTests/Player/PlaybackFreeTeardownRaceTests.swift New playback-free teardown race coverage for sanitizers.
Tests/SwiftVLCTests/Player/PlaybackEndCoordinatorTests.swift New unit tests for end-synthesis coordinator decision table.
Tests/SwiftVLCTests/Player/EventBridgeStressTests.swift Switches to real playback instances + stricter requires.
Tests/SwiftVLCTests/Player/EventBridgeFinalTests.swift Tightens event expectations and uses playback instances.
Tests/SwiftVLCTests/Player/EventBridgeDeepTests.swift Updates deep event tests + includes .endReached string mapping.
Tests/SwiftVLCTests/PiP/PiPVideoViewTests.swift Adds off-main selector-callability tests + auto-start flag tests.
Tests/SwiftVLCTests/PiP/PiPControllerTests.swift Gates PiP playback tests + adds knob/audiosession policy tests.
Tests/SwiftVLCTests/MemoryPressureTests.swift Switches cleanup timing probe to awaitable shutdown().
Tests/SwiftVLCTests/Media/MediaExtendedTests.swift Makes playback/statistics assertions fail-fast with #require.
Tests/SwiftVLCTests/LifecycleStressTests.swift Handles non-startable renderer discovery on tvOS gracefully.
Tests/SwiftVLCTests/Core/BroadcasterTests.swift Adds buffering-policy + re-entrant filter + churn stress tests.
Sources/SwiftVLC/Video/Logo.swift Makes logo pointer “live” via Player reference for handle replacement safety.
Sources/SwiftVLC/SwiftVLC.docc/SwiftVLC.md Adds doc links for integration topology + porting guide.
Sources/SwiftVLC/SwiftVLC.docc/PlaybackEssentials.md Updates volume range + seek API docs + adds live/unknown-duration guidance.
Sources/SwiftVLC/SwiftVLC.docc/IntegrationTopology.md New documentation for “single libVLC per process” topology.
Sources/SwiftVLC/SwiftVLC.docc/AudioFeatures.md Updates volume ceiling explanation to 200%.
Sources/SwiftVLC/Playlist/MediaListPlayer.swift Adds end-synthesis suppression bookkeeping + handle rebind support.
Sources/SwiftVLC/Player/PlayerEvent.swift Adds .endReached event + docs/accessor/description updates.
Sources/SwiftVLC/Player/Player+VideoInfo.swift New videoSize/hasVideoOutput decoded-video surface.
Sources/SwiftVLC/Player/Player+Typed.swift Updates audioVolume doc ceiling to 2.0.
Sources/SwiftVLC/Player/Player+Programs.swift Adds recast(to:) renderer switching + track carry-over matcher.
Sources/SwiftVLC/Player/Player+Overlays.swift Mirrors viewpoint/teletext mutations into per-player state shadowing.
Sources/SwiftVLC/Player/Player+Events.swift Uses unbounded internal stream + adds vout/track-driven invalidations + endReached mirror.
Sources/SwiftVLC/Player/Player+Drawable.swift New drawable attachment + native-handle replacement implementation.
Sources/SwiftVLC/Player/Player+Audio.swift Adds carry-over shadows for audio output module/device selections.
Sources/SwiftVLC/Player/PlaybackValues.swift Raises Volume max to 2.0 + adds SubtitleScale(approximatePoints:).
Sources/SwiftVLC/Player/PlaybackEndCoordinator.swift New coordinator for .endReached synthesis on event thread.
Sources/SwiftVLC/Player/EventBufferingPolicy.swift New per-subscription event buffering policy type.
Sources/SwiftVLC/Player/EventBridge.swift Adds policy-aware streams + endReached synthesis in callback.
Sources/SwiftVLC/PiP/PiPController+Validation.swift Adds SPI validation probe for iOS native PiP backend wiring.
Sources/SwiftVLC/PiP/PiPController+Testing.swift Adds in-module test seams for PiP controller internals.
Sources/SwiftVLC/PiP/PiPController+Events.swift Adds PiP lifecycle event stream + stop-reason model.
Sources/SwiftVLC/PiP/PiPController+Delegate.swift Emits PiP lifecycle events + improves stop-reason resolution.
Sources/SwiftVLC/PiP/PiPController+AudioSession.swift Adds audio-session policy knobs (configure vs deferred activation).
Sources/SwiftVLC/Core/VLCInstance.swift Adds init params for app name / user-agent + adds user-agent/app-id setters.
Sources/SwiftVLC/Core/Logging.swift Fixes potential retain cycle in log broadcaster lifecycle plumbing.
Showcase/tvOS/Internal/TVShowcaseRootView.swift Adds harness route exclusion.
Showcase/tvOS/Internal/TVShowcaseChrome.swift Updates volume slider max to 2.0.
Showcase/tvOS/CaseStudies/10-Diagnostics-Events.swift Uses lossless filtered events subscription API.
Showcase/tvOS/CaseStudies/07-Playlist-Queue.swift Ensures player stop drains via stopAndWait() on disappear.
Showcase/tvOS/CaseStudies/03-Audio-Volume.swift Updates volume slider max to 2.0.
Showcase/SwiftVLCShowcase.xcodeproj/project.pbxproj Switches showcase package reference to local path.
Showcase/Shared/LaunchArguments.swift Adds HarnessHome UI test route.
Showcase/macOS/Internal/MacShowcaseRootView.swift Excludes harness route on macOS.
Showcase/macOS/Internal/MacShowcaseChrome.swift Updates volume slider max to 2.0.
Showcase/macOS/CaseStudies/10-Diagnostics-Events.swift Uses lossless filtered events subscription API.
Showcase/macOS/CaseStudies/07-Playlist-Queue.swift Ensures player stop drains via stopAndWait() on disappear.
Showcase/macOS/CaseStudies/03-Audio-Volume.swift Updates volume slider max to 2.0.
Showcase/iOS/ValidationHarness/streams.local.example.json Adds example stream-config schema for harness.
Showcase/iOS/ValidationHarness/SmokeScreen.swift Adds engine “smoke” diagnostics UI for streams.
Showcase/iOS/ValidationHarness/MatrixScreenG.swift Compares freetype-fontsize option vs runtime SubtitleScale.
Showcase/iOS/ValidationHarness/MatrixScreenF.swift Timeshift seek/jump probes using lenient APIs.
Showcase/iOS/ValidationHarness/MatrixScreenE.swift Background-audio-without-PiP validation screen.
Showcase/iOS/ValidationHarness/MatrixScreenD.swift Cast while PiP + recast end-to-end validation screen.
Showcase/iOS/ValidationHarness/MatrixScreenC.swift Native PiP restore/X baseline + backend probe screen.
Showcase/iOS/ValidationHarness/MatrixScreenB.swift Auto-PiP trigger knob validation screen.
Showcase/iOS/ValidationHarness/MatrixScreenA.swift PiP survival across load() “zap” validation screen.
Showcase/iOS/ValidationHarness/HarnessStreams.swift Loads/bundles operator-provided stream URLs safely.
Showcase/iOS/ValidationHarness/HarnessResultStore.swift Records/exports per-screen validation results via UserDefaults JSON.
Showcase/iOS/ValidationHarness/HarnessHome.swift Harness home UI with availability gating per configured stream.
Showcase/iOS/ShowcaseApp.swift Starts cast trust responder on launch.
Showcase/iOS/Internal/UITestSupport.swift Supports relative log paths under Documents for device log extraction.
Showcase/iOS/Internal/RootView.swift Adds navigation entry to device validation harness.
Showcase/iOS/Internal/CastTrustResponder.swift Auto-accepts cast cert/performance dialogs for harness casting.
Showcase/iOS/Info.plist Adds Local Network + Bonjour keys for Chromecast discovery.
Showcase/iOS/CaseStudies/10-Diagnostics-Events.swift Uses lossless filtered events subscription API.
Showcase/iOS/CaseStudies/07-Playlist-Queue.swift Ensures player stop drains via stopAndWait() on disappear.
Showcase/iOS/CaseStudies/03-Audio-Volume.swift Updates volume slider max to 2.0.
Showcase/iOS/Apps/MusicPlayer/NowPlayingControls.swift Updates volume slider max to 2.0.
scripts/patches/0001-chromecast-hardening.patch Adds cast crash/SIGBUS/subtitle-burn-in hardening patchset.
scripts/fix-duplicate-symbols.sh Localizes duplicate JSON symbols by editing ytdl object, not chromecast.
scripts/check-libvlc-manifest.sh New script to verify per-slice libvlc archive membership manifests.
scripts/build-libvlc.sh Defaults patches dir to in-repo chromecast hardening set.
Fixtures/DynamicHost/verify.sh New fixture build + single-libvlc-copy audit script.
Fixtures/DynamicHost/MediaKit/Sources/FeatureA/FeatureA.swift Adds static feature layer for dynamic-host topology fixture.
Fixtures/DynamicHost/MediaKit/Sources/FeatureB/FeatureB.swift Adds static feature layer for dynamic-host topology fixture.
Fixtures/DynamicHost/MediaKit/Package.swift Defines static feature products consuming dynamic wrapper product.
Fixtures/DynamicHost/MediaCoreKit/Sources/MediaCore/MediaCore.swift Dynamic wrapper re-exporting SwiftVLC, exposes shared instance ID.
Fixtures/DynamicHost/MediaCoreKit/Package.swift Defines dynamic wrapper package that owns SwiftVLC dependency.
Fixtures/DynamicHost/DynamicHost.xcodeproj/xcshareddata/xcschemes/DynamicHost-tvOS.xcscheme Adds shared tvOS scheme for fixture CI build.
Fixtures/DynamicHost/DynamicHost.xcodeproj/xcshareddata/xcschemes/DynamicHost-iOS.xcscheme Adds shared iOS scheme for fixture CI build.
Fixtures/DynamicHost/App/DynamicHostApp.swift Adds fixture app validating single shared VLCInstance at runtime.
Fixtures/DynamicHost/.gitignore Ignores fixture derived data.
.gitignore Ignores operator-supplied streams.local.json + local working docs.
.github/workflows/vendor-manifest.yml New CI gate for libvlc slice-manifest drift.
.github/workflows/test.yml Adds tvOS simulator test job to execute tvOS tests.
.github/workflows/sanitize.yml Updates sanitizer suite filter + documents new teardown-race coverage.
.github/workflows/fixtures.yml New CI gate building fixture topology + auditing libvlc single-copy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/SwiftVLC/Player/EventBridge.swift
@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

The wrapped multi-binding if and test array literals tripped the
trailingCommas, wrapArguments, braces, and opening_brace rules under
--strict. Pure formatting; behavior unchanged.
recast(to:)'s active-session path (handle replacement + restart) and the
populated programs/selectedProgram getters only execute with a live
session, so they were exercised by no test. Add canPlayMedia-gated
integration tests that pin the replay behavior locally. Like the rest of
the playback suite they skip on CI (runners cannot decode media); the
session-free recast paths and the track matcher stay covered by
PlayerCarryOverTests and PlayerRecastTrackMatchTests.
@omaralbeik omaralbeik merged commit 2e637da into main Jun 11, 2026
13 checks passed
@omaralbeik omaralbeik deleted the v0.10.0-dev branch June 11, 2026 02:46
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.

2 participants