i'm the developer of rkmovies, a 4k media player that runs as a web app inside a thin android webview shell. the whole apk is 1.17 mb because it bundles zero native media libraries. playback uses the system's mediacodec decoders through artplayer.
one feature people asked for constantly: picture-in-picture. you open whatsapp mid-episode and the video keeps playing in a floating window.
in a webview you can't call an android api directly. you only get what the browser engine exposes. so the pip toggle in the settings menu is a four-layer fallback chain:
- if document.pictureInPictureElement is set, the video is already in pip. call exitPictureInPicture() to toggle off.
- otherwise try art.video.requestPictureInPicture(). that's the w3c api, and chrome supports it from android 8. inside a webview it gives you the real system pip window with the app icon, not a canvas hack.
- if that's missing (older webview, weird oem rom), fall back to webkitSetPresentationMode("picture-in-picture"), the safari-style api some chromium builds still expose.
- if all three are absent, flip art.pip. that's artplayer's own software pip, which re-parents the video element into a small overlay. it works everywhere but it's not a real system window, so no controls outside the app.
every step is wrapped in try/catch because webview implementations throw different errors instead of returning undefined. i learned that the hard way on a budget micromax device.
the whole chain costs zero bytes of apk size because it only calls apis the platform already has. that's the general pattern in this app: features don't cost size, dependencies do.
if you want to poke at it, this little player lives at this little player, grab it on android at grab it on Android, or take the pc installer from the PC installer
Top comments (3)
This is a masterclass in constraint-based engineering. Shipping a 1.17 MB media player by relying on the system's MediaCodec and zero native libs is incredibly impressive.
The 4-layer fallback chain is the exact kind of progressive enhancement WebView development demands. And the try/catch detail is painfully relatable—OEM WebViews are notorious for throwing weird errors instead of gracefully returning undefined.
Love the philosophy: "features don't cost size, dependencies do." Out of curiosity, how does Artplayer's software PiP hold up on those budget devices compared to the native system PiP? Does it chew through battery faster?
good question. short version: the decode cost is identical, because art.pip never touches the codec.
art.pip is artplayer's mini mode. it creates a fixed .art-mini-popup div on document.body and moves the whole player into it, video element included. so playback still runs through the webview's mediacodec hardware path exactly like fullscreen. what changes is only the render side: system pip (requestPictureInPicture) hands the video to a dedicated surface owned by the compositor, while the mini popup is just a small extra layer the gpu composites every frame, and the screen stays on at full brightness.
on a budget chip (g36/g85 class) that means hw decode in both modes, and the delta is one small composite layer plus a lit screen. from what i've seen on my test devices the decode side is within noise and the total is a few percent extra at most. the real battery eater in both modes is that the display never sleeps while pip is up.
that's why the chain tries system pip first whenever the webview exposes it (chromium webviews on android 8+ do), since the compositor keeps the surface alive even when the app goes background. the art.pip layer is only for oem webviews that don't expose the w3c api, and there the extra cost is the renderer staying active.
That makes perfect sense. I didn't realize art.pip just re-parents the video element, keeping the exact same hardware decode path. So the real delta is just GPU layer compositing and screen brightness. System PiP first makes total sense for keeping the surface alive in the background; the mini-popup is a solid fallback for stubborn OEM WebViews.