1. Discovery & Connection
1. Discovery & Connection
Section titled “1. Discovery & Connection”The problem this solves
Section titled “The problem this solves”Your phone and your TV are two separate computers, sitting on the same Wi-Fi network, that have never talked to each other before. Before your phone can say “play this video,” it needs to answer two questions:
- Which device on this network is a Chromecast, and what’s its name? (“Living Room TV” vs “Bedroom TV”)
- How do I open a connection to it? (an IP address and a port aren’t enough on their own — you need a way to find that IP address first)
This is true of casting in general, not just this app: AirPlay, DLNA, and Chromecast all solve the same “find a device on my LAN” problem, each with its own discovery mechanism. This app uses Chromecast’s, which is built on a general-purpose protocol called mDNS (multicast DNS), also known by the marketing name Bonjour (Apple) or DNS-SD (the underlying standard, “DNS Service Discovery”).
The standard: mDNS / DNS-SD
Section titled “The standard: mDNS / DNS-SD”Normal DNS requires a central server that knows “google.com → 142.250.x.x.”
There’s no such central server for “what’s on my home Wi-Fi right now” —
devices join and leave constantly, and nobody runs a DNS server for a home
network. mDNS solves this by broadcasting the question to everyone on the
local network at once, over a reserved multicast IP address
(224.0.0.251), and letting any device that knows the answer reply.
Every service type has a name. Chromecasts (and Chromecast-compatible devices, Android TVs, etc.) advertise themselves under:
_googlecast._tcp.local "Who is _googlecast._tcp?" ┌───────────┐ ───────────────────────────► ┌───────────┐ │ │ (multicast broadcast, │ │ │ Phone │ everyone on the LAN │ TV │ │ (sender) │ can hear this) │(receiver) │ │ │ ◄─────────────────────────── │ │ └───────────┘ "It's me — 192.168.1.42:8009, └───────────┘ name=Living Room TV"
(any other device on the LAN that isn't a Chromecast simply stays silent)The reply isn’t just an IP address — it comes with a small bundle of
key/value metadata called a TXT record. For Chromecast this typically
includes the device’s friendly name (fn), model (md), and a unique id.
That’s how your phone can show “Living Room TV” in a picker instead of a
raw IP address.
Why this needs to happen every time
Section titled “Why this needs to happen every time”Devices get new IP addresses (DHCP leases expire, routers reboot), Chromecasts get unplugged and moved, and your phone doesn’t keep a permanent registry. So “discovery” is really “ask again, right now, and collect replies for a few seconds.” Give it a window (this app uses 5 seconds), then stop listening and show whatever answered.
t=0s t=5s │ send the mDNS query, start listening │ stop listening, │ │ show the picker ▼ ▼ ├──────────────────────────────────────────────────┤ "Living Room TV" replies at t=0.3s "Bedroom TV" replies at t=1.1sOnce you’ve found a device: opening a connection
Section titled “Once you’ve found a device: opening a connection”Discovery only gets you a name, an IP, and a port (Chromecast always
listens on TCP port 8009). The actual conversation happens over a
TLS-encrypted TCP socket to that IP:port — TLS because Google designed
the protocol to be encrypted in transit, even on a trusted home network.
One quirk worth knowing: Chromecasts use a self-signed certificate
(there’s no public certificate authority for “every Chromecast ever sold”),
so the client has to explicitly accept that instead of validating it the
normal way a browser would validate https://google.com.
Phone TV (Chromecast) │ │ │ TCP connect to 192.168.1.42:8009 │ ├────────────────────────────────────────────────────► │ │ │ TLS handshake (self-signed cert, accepted anyway) │ ◄────────────────────────────────────────────────────┤ │ │ │ ✅ encrypted socket now open │ │ (this is the pipe everything else flows over — │ │ see doc 2 for what actually gets sent on it) │Where this lives in the code
Section titled “Where this lives in the code”local_lib/flutter_cast/lib/discovery_service.dart—CastDiscoveryServicewraps thebonsoirpackage (a Dart mDNS library) to search for_googlecast._tcp, run the timed window, and resolve each reply’s TXT record into a friendly name.local_lib/flutter_cast/lib/device.dart—CastDevice, the plain value object discovery produces:serviceName,name,host,port. Nothing here knows about media or protocols yet — it’s purely “an address and a label.”local_lib/flutter_cast/lib/socket.dart—CastSocket.connect(host, port)is the TLS socket step: it opens the connection and accepts the self-signed certificate.lib/presentation/widgets/player/cast_device_sheet.dart— the UI layer: shows the “pick a device” sheet, runs discovery when it opens, and calls into the connection step when the user taps a device.
The takeaway
Section titled “The takeaway”Discovery and connection are two genuinely separate steps, and it’s worth keeping them mentally separate: discovery is “who’s out there” (a broadcast question, answered over UDP multicast, producing a list of names+addresses), and connection is “let’s talk” (a normal, one-to-one TCP+TLS socket to one specific address you already have). Everything from here on — the actual “play this video” conversation — happens over that one socket, which is the subject of the next doc.