DEV Community

hammi
hammi

Posted on

Audio focus is the feature nobody demos (and every player needs)

You can demo 4K in a trailer. You cannot demo "the movie pauses politely when your mum calls". But users feel that one every single day, and on a player with no account and no telemetry, it has to be handled locally and correctly on the first try.

What actually happens

Android sends audio focus events: another app wants the audio (navigation, a call), you get ducked, transient loss, permanent loss. Ignore them and your movie keeps playing over someone's phone call. The app review then says "ruined my call" and you never hear the end of it.

Bluetooth headphones reconnect with a 1-3 s delay. Wired headsets get yanked mid-scene. AudioManager reports both through focus changes and the becoming-noisy broadcast, but only if you actually registered for them.

On the Windows build the same problem exists inside WebView2, except the OS audio session ducking does not come for free. You wire it yourself or you do not get it.

How I did it without a media framework

I skipped media3/exoplayer partly to keep the APK at 1.17 MB, which means audio focus is my code, not a library's. The core of it is one OnAudioFocusChangeListener:

  • permanent loss: pause and remember position
  • transient loss: pause, resume on regain, with a 30 s guard so a notification blip does not restart playback
  • duck: drop volume to 20% instead of pausing, like a good citizen
  • becoming noisy (headset unplug): pause immediately, because the user cannot hear the movie anymore anyway

Bluetooth is the annoying one. An A2DP reconnect can take a couple of seconds and some OEMs fire the noisy event twice. So I debounce: only pause if the event is still true 500 ms later.

Position restore is one IndexedDB row per file (profile|path|se|ep -> positionMs). On focus regain I seek back to it. That is the whole feature. Maybe 400 lines including the Windows side.

Where it still breaks

Some Chinese OEM battery savers (Xiaomi, Realme) kill the process outright instead of pausing it. Nothing in-app fixes that. The fix is "disable battery saver for the app", and I cannot do that for the user.

During a real phone call Android sometimes hands focus back before the call has actually ended. My 30 s guard catches most of it, not all of it.

And the WebView2 side still does not duck automatically on Windows. I have to hook the core audio session events myself. That one is honestly still on my todo list.

The numbers I keep quoting: the APK is 1.17 MB, the Windows installer is 29.5 MB, playback stays under 15 MB RAM because decode is hardware (MediaCodec on Android, NVDEC/QuickSync on PC). Audio focus costs zero bytes of that. It is just correct event handling.

If you want to try the player: the player's home page is the start, the android download page has the APK, and the pc installer page has the Windows build. I'm the developer, fyi.

Top comments (4)

Collapse
 
alexcodebytes profile image
Oleksandr •

Audio focus is definitely the hidden MVP of media players. The 500ms debounce for Bluetooth reconnects is a clever touch, and the OEM battery saver struggle is painfully relatable. Good luck tackling the WebView2 ducking!

Collapse
 
hammiwor profile image
hammi •

appreciate you reading. same lesson on my side: the boring approach (keep the player instance alive, swap only the track) beat every clever re-init i tried.

Collapse
 
alexcodebytes profile image
Oleksandr •

Keeping the player instance alive is especially true for audio focus. If you tear down the audio session on every track change, you drop the focus lock and the OS quietly decides your background playback doesn't matter anymore.

Thread Thread
 
hammiwor profile image
hammi •

good point on Keeping the player instance alive is esp. i measured something similar on my own build and it changed how i think about startup work