v0.10.0 — lifecycle/seek/PiP rework and working Chromecast casting#55
Merged
Conversation
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>
There was a problem hiding this comment.
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
.endReachedsignal 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.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
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.
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.
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
CI=true(F041, F074, P1.13, P2.19).set_mediaswap (no phantom.endReached).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 bybuild-libvlc.shby default):Open()falls back to local playback).DEMUX_GET_LENGTHpasses the live flag the adaptive demuxer reads.CC_ENABLE_SPU(upstream#if 0'd it); a selected subtitle forces transcode +soverlayblend onto the cast stream.Player.recastcarries audio/subtitle selection across the session restart (matches by language/name since ids are session-scoped), with unit tests for the matcher.Verification
CI=true swift test --no-parallel)Release note for the binary
Vendor/libvlc.xcframeworkis gitignored; the committedPackage.swiftreferences the released binary by url+checksum. Runningrelease.sh 0.10.0rebuilds/zips the xcframework, computes the checksum, and bumpsPackage.swift— that step carries the cast fix into the published binary.🤖 Generated with Claude Code