dart_cast
A pure Dart cross-platform casting package supporting Chromecast (CASTV2), AirPlay, and DLNA with built-in HTTP proxy for header injection.
0.7.4
3.4K downloads/mo
#4399 most downloaded on pub.dev
abdelaziz-mahdy/dart_cast
What this package is like to depend on
Last release 7 days ago
16 Aug 2026
Ships fairly regularly
a new release about every 5 weeks
Nearly every release is documented
notes for 14 of 14 stable releases
Nothing withdrawn
no release was ever pulled
5 months old
14 releases · first in 2026
14 releases in the last 12 months
see the full history below
Release timeline
14 releases · Mar 2026 to Aug 2026Releases
latest 14-
0.7.416 Aug 2026Release notes
Open source →What's Changed
- fix(chromecast): default subtitle honor, stale LOAD corpse attribution, resume track re-assert by @abdelaziz-mahdy in #19
Full Changelog: v0.7.3...v0.7.4
Release notes
Open source →New
CastMedia.defaultSubtitlenames the subtitle track playback starts with (matched by URL againstsubtitles). Honoured by Chromecast (activeTrackIdson LOAD) and DLNA (picked oversubtitles.first)
Fixed
- Chromecast no longer activates the first subtitle track when none was chosen — the track-list order silently picked the on-screen language (a French-first list produced French subtitles regardless of what the app showed as selected). No
defaultSubtitlenow means no subtitle shown - Chromecast re-asserts the active subtitle after every pause → resume, including pauses from the TV remote. The Default Media Receiver can stop rendering cues after a pause/resume cycle while still reporting the track active; toggling the track set (deactivate + reactivate) re-attaches its renderer, which is what users were doing by hand
- Chromecast LOAD retries are no longer failed by the previous attempt's dying receiver session. An unsolicited
IDLE/ERRORbroadcast from a session that neither answers the in-flight LOAD'srequestIdnor matches its session id is now ignored as a stale corpse (and its session deprecated). Observed live: a next-episode LOAD was declared failed 1ms after being sent — the corpse of the failed first attempt — while the receiver went on to play the episode fine, leaving the app convinced the old episode was still playing
-
0.7.325 Jul 2026Release notes
Open source →New
MediaSourcereaders may now open asynchronously — anasyncfunction satisfies the reader, so sources backed by Androidcontent://URIs, network handles or a decrypt handshake no longer need aStream.fromFuture(...)workaround (#12). Existing synchronous readers are unaffected; anasync*reader needs a declared return type, which theMediaSourcedocs show
-
0.7.225 Jul 2026Release notes
Open source →What's Changed
- fix: offer AirPlay 1 to receivers that advertise both video versions by @abdelaziz-mahdy in #18
Full Changelog: v0.7.1...v0.7.2
Release notes
Open source →Fixed
- AirPlay: receivers advertising both AirPlay 1 and AirPlay 2 video (Apple TVs, the macOS receiver) now get one AirPlay 1 attempt if the AirPlay 2
/playreturns 404. 0.7.0 sent every AirPlay 2 device down the V2 path and never offered V1, so a device implementing only the older endpoints could not play at all. The retry is gated on the device advertising bit 0 — it is not a return of the blind version probe 0.7.0 removed
-
0.7.125 Jul 2026Release notes
Open source →Documentation only — no code changes.
- AirPlay's status, the reason video fails on the receivers tested, and how to check your own device moved out of the README into
doc/AIRPLAY.md - Protocol status is scoped to the devices actually tested rather than reading as a verdict on AirPlay itself; results from other hardware, an Apple TV especially, are welcome
- The feature table distinguishes "implemented but blocked by the receiver" from "not built yet"
- AirPlay's status, the reason video fails on the receivers tested, and how to check your own device moved out of the README into
-
0.7.025 Jul 2026Release notes
Open source →What's Changed
- fix: report HLS duration and make seeking work on DLNA renderers by @abdelaziz-mahdy in #14
- fix: make the AirPlay 2 video path real — capability-driven, transiently paired, with a timing server and /rate by @abdelaziz-mahdy in #13
- fix: retry a DLNA SOAP action when the renderer drops the connection (#9) by @abdelaziz-mahdy in #15
- feat: serve caller-supplied byte sources through the proxy (#12) by @abdelaziz-mahdy in #16
- test: make the flaky-renderer mock respond once per connection by @abdelaziz-mahdy in #17
Full Changelog: v0.6.0...v0.7.0
Release notes
Open source →AirPlay 2 and DLNA fixes plus caller-supplied byte sources, driven by testing against a TCL Google TV.
Breaking
-
AirPlay selects its protocol version from the advertised feature bits instead of probing
/play; devices with no video bit throwUnsupportedFeatureExceptionimmediately -
AirPlaySession.connect()pairs with every AirPlay 2 receiver, so it can now throwNeedsPairingExceptionwhere it previously returned a session. If you callconnect()on an AirPlay 2 device and do not catch that exception, this release will start throwing at you. The old behaviour was not useful — the session it returned had fallen back to a legacy path those devices reject, so it could not cast anything — but it did not fail either. Handle it by prompting for the on-screen passcode and callingpairSetup(pin), or by catching it and offering Chromecast/DLNA instead:try { await session.connect(); } on NeedsPairingException { // Show the code on the TV, then: await session.pairSetup(pinTheUserTyped); await session.connect(); }Most smart TVs use PIN-less transient pairing and never reach this path; it is devices that demand a passcode which now surface it.
-
AirPlayMediaController.dispose()returnsFuture<void>;play()takesstartPositionSeconds:instead ofstartPosition:
Fixed
- DLNA: seeking works when casting HLS — it previously stopped playback outright
- DLNA: HLS casts report the real duration instead of one second
- DLNA: a playback command no longer crashes with
ClientException: Connection closed before full header was receivedwhen the renderer drops an idle connection — the action is retried once (#9) - DLNA: a renderer that accepts a connection then stalls fails after 10s instead of hanging the caller forever
- DLNA: routine connection churn no longer floods the log with errors
- AirPlay: AirPlay 2 receivers pair and establish a session correctly — PIN-less transient pairing, RTSP setup and keep-alive, all confirmed against a real TV. The package previously fell back to a legacy path these devices reject
- AirPlay: failures report a cause instead of being swallowed — an unsupported device, a rejected handshake or a receiver-reported error now surfaces
- AirPlay: further video-path corrections (protocol version selection, playback-state parsing, request ordering,
startPosition). Unverified end to end — no receiver tested accepts a video URL, so nothing past the handshake can be exercised yet
New
MediaSource+MediaProxy.registerSource()+CastMedia.source()— cast bytes the package cannot open itself: Androidcontent://URIs, Flutter assets, decrypted or in-memory content (#12). The application supplies a range-aware reader; the proxy handles HTTP, byte ranges and seeking. Works on Chromecast, DLNA and AirPlay, since it uses the same route local files already useAirPlayTimingServer, transient pairing,PlaybackInfo.errorHlsParser.totalDuration/extractSegments,MediaProxy.probeHlsDuration/parseTimeSeekRangetool/airplay_probe.dart,tool/airplay_hardware_check.dart,tool/dlna_hardware_check.dart,tool/chromecast_hardware_check.dart— hardware verification scripts
Known limitations
- AirPlay video does not play on the TV tested and cannot: that receiver exposes no
/playendpoint, and Apple's own QuickTime fails on it identically — use Chromecast - DLNA sidecar subtitles did not render on the TV tested; embed SRT in MKV
-
0.6.029 May 2026Release notes
Open source →What's Changed
- feat: alt-audio HLS remuxer + Chromecast LOAD diagnostics (0.6.0) by @abdelaziz-mahdy in #10
Full Changelog: v0.5.1...v0.6.0
Release notes
Open source →New
- Alt-audio HLS sources (split video + audio) now play on Chromecast and DLNA — pure-Dart MPEG-TS remuxer combines them into one muxed stream
MediaProxy.registerAltAudioMuxed()— synthetic single-stream HLS master backed by per-segment muxingHlsParser.extractAudioRenditions+audioGrouponextractVariantsentries- TS DVB-table stripper for proxied
video/mp2t, on by default; opt out viaregisterMedia(stripDvbTables: false) MediaLoadFailedExceptionfromloadMedia()onLOAD_FAILED/IDLE+ERROR/ 15s timeout, carrying the receiver-reported reason- HLS LOAD retries pass-through → muxer + stripper on failure, with
requestId/mediaSessionIdfiltering against stale responses ChromecastSession.enableReceiverDebugNamespaces(defaultfalse) — opt-in CaC + debugoverlay subscribe and verbose receiver logging for debugging
Fixed
setSubtitle()activates the correct track (was alwaystrackId=1)- CORS
Access-Control-Allow-Originechoes the receiverOriginwhen the LOAD includes atracksarray - State machine accepts
loading → {buffering, paused, idle}andidle → {buffering, playing, paused} - DLNA: failed
loadMediareturns toidleinstead of stuck inloading Content-Lengthonly forwarded when the body streams through unchanged- Proxy segment / subtitle URLs end in
.ts/.vttfor the Chromecast URL-extension probe image/*Content-Type on proxied MPEG-TS subresources rewritten tovideo/mp2t
-
0.5.111 Apr 2026Release notes
Open source →Patch release to reclaim the 10 pub points pana docked on 0.5.0's static-analysis check (40/50 → 50/50, total 160/160).
No API, behavior, or dependency changes — pure lint cleanup under `lib/`.
Fixed
- `curly_braces_in_flow_control_structures` (×2) — wrap single-statement `if` bodies in braces in `ts_keyframe_scanner.dart` and `airplay/auth/airplay_auth.dart`.
- `use_super_parameters` (×2) — the two `CastMessage_*` enum constructors in `chromecast/proto/cast_channel.dart` now use super-parameter syntax.
- `camel_case_types` (×2) — suppressed via a file-level `// ignore_for_file` on `cast_channel.dart`, because the `CastMessage_ProtocolVersion` / `CastMessage_PayloadType` identifiers must stay byte-for-byte compatible with what `protoc --dart_out` emits for Chromium's `cast_channel.proto`.
See #8 for the full diff and CLAUDE.md pre-PR checklist run used to verify.
What's Changed
- chore: fix remaining pana lints for 160/160 pub points (0.5.1) by @abdelaziz-mahdy in #8
Full Changelog: v0.5.0...v0.5.1
Release notes
Open source →Fixed
- Pub points (static analysis): Resolved the six pana lints under
lib/that kept the 0.5.0 static-analysis score at 40/50:curly_braces_in_flow_control_structuresints_keyframe_scanner.dartandairplay/auth/airplay_auth.dart— single-statementifbodies now wrapped in braces.use_super_parameterson the twoCastMessage_*enum constructors — converted to super-parameter syntax.camel_case_typeson the hand-writtenCastMessage_ProtocolVersion/CastMessage_PayloadTypebindings inchromecast/proto/cast_channel.dart— suppressed via a file-level// ignore_for_file: camel_case_types, since these identifiers intentionally mirror the namesprotoc --dart_outwould emit for Chromium'scast_channel.proto.
-
0.5.011 Apr 2026Release notes
Open source →Closes #6.
Changed
- protobuf: Upgraded from
^3.1.0to^6.0.0for the latest performance improvements and bug fixes from theprotobuf.dart4.0 → 6.0 releases. Hand-written CastMessage bindings use only stableGeneratedMessage/BuilderInfo/PbFieldTypeAPIs, so no code changes were needed —CastMessagealso lives underlib/src/and is not re-exported, so the major bump does not leak into this package's public API. - Minimum Dart SDK: Bumped
sdkconstraint from^3.0.0to^3.7.0(required byprotobuf4.1.1+). The SDK bump also switches the default formatter to the Dart 3.7 tall style, so thelib/andtest/tree was reformatted in this release.
Fixed
- Dropped two unnecessary
!non-null assertions incast_service.dartandairplay/auth/airplay_auth.dartthat a newer analyzer (pulled in by the SDK bump) started flagging as warnings. example/: theImage.networkerrorBuildersignature now uses Dart 3.7 wildcard parameters ((_, _, _)) instead of multiple underscore names.
Docs
- Added
CLAUDE.mdwith a pre-PR verification checklist that replays every CI step locally and calls out three gotchas that tripped this release (warning-level analyzer exits, formatter style flips on SDK bumps, andflutter analyzebeing stricter thandart analyze).
What's Changed
- chore: upgrade protobuf to ^6.0.0 (0.5.0) by @abdelaziz-mahdy in #7
Full Changelog: v0.4.3...v0.5.0
Release notes
Open source →Changed
- protobuf: Upgraded
protobufdependency from^3.1.0to^6.0.0for the latest performance improvements and bug fixes (#6). Hand-written CastMessage bindings remain compatible; no API changes. - Minimum Dart SDK: Bumped
sdkconstraint from^3.0.0to^3.7.0(required byprotobuf4.1.1+).
- protobuf: Upgraded from
-
0.4.305 Apr 2026Release notes
Open source →What's Changed
- fix: analyzer warnings + bump to 0.4.3 by @abdelaziz-mahdy in #5
Full Changelog: v0.4.2...v0.4.3
Release notes
Open source →Fixed
- Analyzer warnings: Resolved pre-existing unused variable warnings in test files
- Code formatting: Applied
dart formatto all source files
-
0.4.205 Apr 2026Release notes
Open source →What's Changed
- feat: local file casting, MediaTransformer refactor, ffmpeg reference by @abdelaziz-mahdy in #1
- docs: protocol status disclaimers and 0.3.1 by @abdelaziz-mahdy in #2
- feat: DLNA overhaul — HTTP/1.0 file server, MKV subtitles, seeking by @abdelaziz-mahdy in #3
- fix: proxy IP selection, concurrent loadMedia, socket disconnect by @abdelaziz-mahdy in #4
New Contributors
- @abdelaziz-mahdy made their first contribution in #1
Full Changelog: v0.2.0...v0.4.2
Release notes
Open source →Fixed
- Proxy IP selection: MediaProxy now picks the local interface on the same subnet as the target cast device, fixing casting failures on Android where the proxy would bind to a non-WiFi address unreachable by the cast device
- Concurrent loadMedia guard: All protocol sessions (Chromecast, AirPlay, DLNA) now ignore duplicate
loadMedia()calls while one is already in progress, preventing multiple LOAD messages from being sent to the device - Socket disconnect detection: Chromecast session now handles message stream errors and unexpected closures, transitioning to disconnected state so the app can react (previously the session stayed "connected" after a network drop)
-
0.4.121 Mar 2026Release notes
Open source →Fixed
- DLNA start position: seek now deferred until TV confirms PLAYING state (immediate seek was ignored by TVs still loading)
- Chromecast subtitles for local files: subtitle files now served via HTTP/1.1 with CORS headers (HTTP/1.0 path lacked
Access-Control-Allow-Originrequired by Shaka Player) Http10FileServer: returns 416 Range Not Satisfiable for invalid/out-of-bounds rangesSubtitleConverter.vttToSrt(): uses targeted regex for timestamp dots (no longer corrupts non-timestamp content), expands 2-component MM:SS timestamps to HH:MM:SS- Removed stray
response.close()after socket detach in synthetic content handler
New
- Updated example app with optimistic slider state, keyboard shortcuts, mute toggle, and responsive layout
- Added protocol feature indicators in example device picker
- Updated README with "What Works Where" feature matrix, protocol notes, and DLNA local files guide
-
0.3.119 Mar 2026Release notes
Open source →- Added protocol status table to README with testing coverage and known limitations
- Documented DLNA MP4 playback issues (TV-dependent, some reject proxy-served MP4)
- Documented DLNA subtitle limitations (
sec:CaptionInfoExnot universally supported) - Documented AirPlay video casting limitations (404 on some Google TV devices)
- Recommended Chromecast as the primary tested protocol for local file casting
-
0.3.019 Mar 2026Release notes
Open source →Breaking changes
DefaultMediaTransformerno longer wraps local TS files in HLS — it serves them directly via the proxy. UseTsHlsMediaTransformerorFfmpegMediaTransformerfor Chromecast-compatible local TS casting.CastMedia.useChunkedHlsis deprecated and will be removed in a future release.TsHlsMediaTransformeralways uses chunked HLS.ChromecastSessionnow defaults toTsHlsMediaTransformerinstead ofDefaultMediaTransformer.
New
TsHlsMediaTransformer— wraps local TS files in keyframe-aligned HLS playlists for Chromecast compatibilityMediaProxy.setPatPmt()/MediaProxy.setFirstPts()— enable correct PAT/PMT prepending and PTS offset for virtual HLS segmentsFfmpegMediaTransformerreference implementation in example app — remuxes TS→MP4 via ffmpeg with progress callbacks and mobile platform supportdoc/LOCAL_FILE_CASTING.md— comprehensive guide covering remux, HLS wrapping, and transcode approaches with tradeoffs
Migration guide
Replace direct
DefaultMediaTransformerusage for local TS files:// Before (0.2.x) — DefaultMediaTransformer handled local TS→HLS internally final session = await device.connect(); // After (0.3.0) — choose your transformer explicitly // Option A: FFmpeg remux (recommended) final session = await device.connect( mediaTransformer: FfmpegMediaTransformer(), ); // Option B: Built-in HLS wrapping (no external tools) final session = await device.connect( mediaTransformer: TsHlsMediaTransformer(), ); -
0.2.017 Mar 2026Release notes
Open source →- AirPlay feature flag detection via mDNS TXT records (
AirPlayFeaturesclass parsesfeatures/ftbitmask) AirPlayMediaControllerwith V1/V2/playformat auto-negotiation (V1 binary plist → V1 text/parameters → V2 with RTSP SETUP)UnsupportedFeatureExceptionthrown immediately when a device lacks video support bits (0 and 49)PlaybackExceptionthrown when all/playformat attempts are rejected by the device- Breaking:
HapSessionno longer hasplay,stop,scrub, orratemethods — useAirPlayMediaControllerinstead - Added
doc/PROTOCOL_REFERENCES.mdwith links to AirPlay, Chromecast, and DLNA specs - Added
doc/FUTURE_WORK.mddocumenting AirPlay screen mirroring and RAOP audio streaming roadmap
- AirPlay feature flag detection via mDNS TXT records (