Back to Feed
article 3m read

Earbuds kept beating my TV audio guard, so I stopped guarding the setting

Three races between my audio guard and wireless earbuds over the system default output got patched one by one; then I pinned the movie player to the TV directly and deleted most of the patches.

Article DepthPractice

Flow

Pinning the player to the TV

  1. 1Grant one permission

    The helper lets the page choose speakers and never gives it the microphone.

  2. 2Find the TV

    Refuse the generic default output and require exactly one output with the TV's name.

  3. 3Bind before play

    Every play waits until the video is bound to the TV with setSinkId.

  4. 4Ignore device changes

    The player's reactions to device changes and media keys are switched off.

  5. 5Keep the backup

    Built-in speakers stay muted, and the helper finds the TV and speakers by saved IDs instead of listing every device.

Every time a pair of wireless earbuds woke up near the box behind my TV, my code and the operating system fought over where the sound should go. My code kept losing.

The box is a small computer that plays movies in a browser on the TV. My rule is that sound comes out of the TV and nowhere else. Not the computer's speakers, and not someone's earbuds.

First try: guard the system setting

The computer has one system-wide setting for where sound goes. When I pressed Play, my helper set it to the TV, muted the built-in speakers, and started a guard that checked it a couple of times a second. The player had a short lease the helper kept renewing. If anything looked wrong, the lease stopped and the player paused and muted until the next Play. I also told the guard to switch back to the TV if earbuds grabbed the setting.

That worked until the earbuds and the guard started racing.

The race

The guard read the list of devices, then read the setting. If earbuds connected between those two reads, the list didn't have them but the setting pointed at them. The guard thought the route was broken and stopped the movie for good, until someone pressed Play.

I patched it a few times. Read the setting before and after the list, and retry if it moved. Skip a device that vanished mid-read, but only if it's really gone. Let the guard fix a setting that was briefly empty, as long as the speakers were muted. Each patch was right, and each added more reads, which meant more gaps to lose in.

Second try: pin the player

So I stopped guarding the setting. Browsers let a page pick the output for each video, so the player now picks the TV and ignores the system setting completely:

That one call is all the player needs to say where its sound goes.

The helper gives the page one permission, choosing speakers, and never the microphone. The page refuses the generic "default" output and needs exactly one output with the TV's name. Every play waits until the video is bound to the TV. The player's reactions to device changes and media keys are switched off, so earbuds can't pause it either. The speakers stay muted as a backup. And the helper now looks up the TV and speakers by saved IDs instead of checking every device, since reading each device's name and properties is what stalls while earbuds connect.

My favorite test keeps firing device changes and keeps trying to move the movie to the earbuds. The movie never stops and never leaves the TV. That same change deleted most of my race patches.

Not tried on real earbuds yet

I've only tested this with fake devices, not a real unplug with real earbuds. It protects one app. Anything else on the box that makes sound has only the speaker mute standing between it and the room.

Two follow-up fixes

Two follow-up commits cleaned up the edges. The new guard replaces an old one that might have already muted the movie, and it could have kept that mute as if I'd chosen it. Now it lets the old guard save the real state first. Then some browser builds rejected an ID they had handed out themselves, which killed the permission setup. The helper now records which step failed and retries once without that ID.

Keep reading

Pick up a connected idea or branch into a nearby one.

3 paths forward