Skip to content

About

Picture in picture for Android TV: your own camera (a baby monitor, the front door) or stream plays in a corner over any app. A Clean Your TV add-on.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

1 watching

Forks

Repository files navigation

Corner Player Logo

Picture in picture for Android TV: your own camera, stream or HDMI input, in a corner, over any app

Android TV 8+ License Sources: RTSP, HLS, M3U, HDMI No channels included

Corner Player

Corner Player is an open-source picture-in-picture app for Android TV. Your own camera (a baby monitor, the front door), a stream from your own M3U playlist or one of the TV's HDMI inputs plays in a small window in a corner, over whatever you're watching: any app, Live TV or an HDMI input. The window never takes the remote's keys, so the app below keeps working as before, and it is silent unless you turn its sound on.

It is for your own cameras and streams: it comes with no channels and points to none, and it only plays the addresses you give it.

Screenshots

Corner Player on a TV, playing a test stream over Home

The video in the corner is a public HLS test stream of Big Buck Bunny (© Blender Foundation, CC BY 3.0). App banners belong to their owners.

Features

  • Over anything: Android TV's own picture in picture only works for apps that support it; Corner Player plays its own sources, so it works over any app, Live TV or an HDMI input
  • Your cameras: rtsp:// and http(s):// cameras by name; add them on the TV or push them from a computer, so passwords are never typed on the TV
  • Your playlist: the channels of an M3U playlist you point it at
  • HDMI inputs: on a TV with HDMI inputs, any of them (a game console, a set-top box, a camera's HDMI out) by the name the TV gives it
  • Network control: Home Assistant, a doorbell or any device on your network opens a camera or input in the corner with an HTTP request, for as long as you like, and closes it; off unless you switch it on, and every request needs the TV's token
  • Snapshot and MJPEG cameras: a camera address that answers with a picture is refreshed every second, and an MJPEG stream is shown picture by picture; neither needs a video decoder, so they play even where a second video can't
  • Cycle cameras: every camera in turn, 5 to 60 seconds each, like a security monitor
  • Reopen after restart: optionally, what was in the corner comes back when the TV starts
  • Any corner, three sizes: bottom right (default), bottom left, top left, top right; 25, 33 (default) or 40 % of the screen's width, 16:9
  • Hands off the remote: the window never takes focus or keys; the app below keeps working
  • Silent by default: sound off unless you switch it on
  • Passwords stay hidden: on screen, addresses show only the scheme, host and port
  • Private: connects only to the addresses you give it, and listens only when you switch network control on; no telemetry, no update checks, no ads
  • Playback by Media3 (ExoPlayer), with its HLS and RTSP modules
  • A module card for the Clean Your TV launcher ("Corner Player", orange glow); on any other home it's an ordinary app

Requirements

  • An Android TV or Google TV device on Android 8 or newer
  • Your own camera with an RTSP or HTTP stream, or your own M3U playlist, on a network the TV can reach; or, on a TV, something plugged into one of its HDMI inputs
  • To install: a computer with ADB and Developer options → Network debugging on the TV (the installer also allows the corner window, which many TVs hide)

Installation and Setup

From a release (recommended)

  1. Download CornerPlayer-x.y.z.apk from the latest release.
  2. Install it, allow its corner window and open it:
    TV_HOST=<tv ip> APK=CornerPlayer-0.1.0.apk ./install.sh [config.json]
    

From source

JDK 17 and Android SDK platform 34 with build-tools 34.0.0:

TV_HOST=<tv ip> ./install.sh [config.json]

It builds the app, installs it with adb, allows the corner window and opens it. Where macOS blocks the stock adb, set CLEANYOURTV to a clone of Clean Your TV with its tools set up, and its tools/tv.py is used instead. To build only: ./gradlew assembleRelease; the JVM tests run with ./gradlew testReleaseUnitTest.

Adding cameras

Cameras usually publish RTSP: look for "RTSP" in the camera's app or manual. A sub-stream (smaller picture) is the better choice for a corner, for example rtsp://user:password@192.168.1.20:554/stream2. Type the address on the TV, or push cameras and the playlist from a computer as a file, so camera passwords are never typed on the TV or put on a command line:

cat > backup/corner-player.json <<'JSON'
{"cameras": [{"name": "Nursery", "url": "rtsp://user:password@192.168.1.20:554/stream2"}],
 "playlist": "http://192.168.1.5:8080/playlist.m3u"}
JSON
TV_HOST=<tv ip> ./install.sh backup/corner-player.json

The app takes the file into its own storage and deletes it. Keep the file out of git (backup/ is git-ignored).

Usage

Open Corner Player (or its card) and press OK on a camera, channel or input: it starts in the corner and you're back to what you were watching. Open it again to switch, or press Stop.

RowWhat it does
StopCloses the corner window (shown while one plays, focused first).
CamerasYour cameras by name; OK plays one. Add a camera asks for a name and an rtsp:// or http(s):// address; Remove cameras (OK twice) removes them all. With two or more, Cycle cameras shows each in turn; ◀ ▶ sets 5 to 60 seconds each. A camera address can also be its snapshot (a picture) or MJPEG address.
ChannelsThe channels of your playlist; OK plays one. Playlist address is an M3U playlist.
InputsThe TV's HDMI (and other) inputs, by the names the TV shows; OK plays one. Boxes and sticks have none.
Network controlOff (default) or On. On, it shows this TV's address and the token your automations send; New token (OK twice) replaces it.
CornerBottom right (default), bottom left, top left, top right.
Size25, 33 (default) or 40 % of the screen's width, 16:9.
SoundOff (default) or On.
Reopen after restartOff (default) or On: what was in the corner (not a short one from the network) comes back when the TV starts. Stop forgets it.

Network control

Switch on Network control and Corner Player listens on port 8765 for requests from your home network: a doorbell press shows the front door, Home Assistant shows the nursery when it hears crying. The automation stays in your home server; the TV only opens and closes the window. Every request carries the token shown on the TV (or pushed as "token" in config.json, with "remote": true).

RequestWhat it does
POST /play with name=Opens a camera, input or playlist channel by its name (any case), for example name=Front door or name=HDMI2.
POST /play with url=Opens any rtsp:// or http(s):// stream; title= is the name it shows.
POST /play with cycle=Shows every camera in turn, that many seconds each.
seconds=With any of them: it ends by itself after that long (up to 3600), and the corner goes back to what was in it before, or closes if it was empty. A doorbell over the baby monitor, then the baby monitor again. Without it, it stays up until stopped.
corner=, size=, sound=With any of them: this window only, for example corner=top-left, size=50 (15 to 60 % of the screen's width), sound=on. Without them, the app's settings.
POST /stopCloses the window.
GET /status{"playing": "Front door", "cycling": false}; playing is null when the corner is closed.

Parameters go in the query, a form body or a JSON body. Replies are JSON: {"ok": true, "playing": ...} or {"error": ...} with status 400, 401 (no or wrong token), 404 (no such camera or input) or 409 (the window isn't allowed).

curl -X POST -H "Authorization: Bearer <token>" \
  -d "name=Front door" -d seconds=60 http://<tv ip>:8765/play

In Home Assistant, a rest_command (keep the token in secrets.yaml):

rest_command:
  tv_corner_play:
    url: "http://<tv ip>:8765/play"
    method: post
    headers:
      Authorization: !secret corner_player_auth   # "Bearer <token>"
    content_type: "application/json"
    payload: '{"name": "{{ name }}", "seconds": {{ seconds | default(60) }}}'
  tv_corner_stop:
    url: "http://<tv ip>:8765/stop"
    method: post
    headers:
      Authorization: !secret corner_player_auth

Then an automation calls rest_command.tv_corner_play with name: Front door when the doorbell rings. Give the TV a fixed address in your router so automations keep finding it.

How It Works

Corner Player draws a small, always-on-top window (Display over other apps) that never takes focus, so key presses keep going to the app underneath. A foreground service plays the stream into it with Media3 while the window is up, and stops with it. An HDMI input plays through Android's TV input framework (a TvView), the way the TV's own Live TV or Inputs app shows it; it needs no extra permission.

PermissionWhy
INTERNETThe cameras and playlist you add, and nothing else.
Display over other apps (SYSTEM_ALERT_WINDOW)To draw the corner window over other apps. It never takes focus or the remote's keys. Granted by install.sh, since many TVs hide the setting.
Foreground service (FOREGROUND_SERVICE, FOREGROUND_SERVICE_MEDIA_PLAYBACK)One service plays the stream while the window is up, and stops with it.
FOREGROUND_SERVICE_SPECIAL_USE, RECEIVE_BOOT_COMPLETED, ACCESS_NETWORK_STATENetwork control only, while it's on: a second service listens for your automations and comes back after a restart; the TV's address is shown on screen.

Plain http:// and rtsp:// are allowed for the home network (res/xml/network.xml). ConfigProvider takes a pushed file and only the ADB shell can use it (it requires WRITE_SECURE_SETTINGS). More in SECURITY.md.

Known Limitations

  • Two videos at once need two decoders. The corner and the app below each decode a video. Many TV chips have two decoders; some don't, or share them badly: on one test TV a second stream next to YouTube made YouTube stall. Prefer a camera's sub-stream, or its snapshot or MJPEG address, which needs no decoder, and check with what you watch.
  • Protected video (some streaming apps' DRM) may refuse to play while another window shows video, depending on the chip.
  • A pointer can't click through the window. Since Android 12 an overlay that lets touches through is drawn at most 80 % opaque, which would show what's underneath the video, so the corner window keeps touches. TVs have no touchscreen; with an air mouse, that one corner can't be clicked through.
  • HDMI is up to the TV. Corner Player shows the inputs the TV lists for apps; some makers list none, or refuse a second app. Most TVs can show only one HDMI input at a time, so an HDMI input in the corner over another HDMI input (or over the TV's own tuner) usually won't work; over an app it should. With nothing on the input, the window says "No picture" and waits. An input's window has square corners and no shadow: many TV chips show a black window otherwise. Its name stays on it until the TV reports a picture, so on a black window, a name that stays means the TV sends no picture, and a name that fades means the TV says it does (please open an issue with the TV's model). On some chips (Realtek) a screenshot shows the HDMI picture's place wrongly; the TV itself shows it in the corner.
  • Network control is plain HTTP on your home network: the token keeps others on the network out, but anyone who can read your network traffic can read it too. Never forward port 8765 to the internet; from away, use a VPN to your home network.
  • Snapshot cameras are asked for a new picture every second, and a user name and password in their address are sent as Basic authentication; cameras that only take Digest authentication for snapshots won't send one.
  • A stream that fails is retried once; then the window says it can't play and closes after a few seconds.

Tested On

MOKA R8G (Android 14), on a home app that implements the module-card spec: a public HLS test stream from a camera entry and the same kind of stream from an M3U playlist served on the home network, playing silently in the bottom-right corner over Home; the corner window opaque; Stop. A second video playing underneath (YouTube) was started but not confirmed, so check with what you watch. RTSP was not tried with a real camera. The signed v0.1.0 release APK installed with install.sh (APK=), corner window allowed. HDMI inputs (added for #1, v0.2.0): the same TV lists AV and HDMI1 to HDMI3; HDMI1 and HDMI2 opened in the bottom-right corner over Home with nothing plugged in, saying "No picture on HDMI1", and Stop let go of the input. A picture from a plugged-in device was not tried yet. v0.4.0's snapshot and MJPEG cameras, cycling, per-window corner, size and sound, the corner going back after a short one, channels by name over the network and Reopen after restart build and pass the tests, but were not tried on a TV yet. Network control (added for #3, v0.3.0), on the same TV with Corner Player in the background over Home: requests without or with a wrong token refused (401), unknown names, bad addresses, paths and methods answered with their errors; POST /play opened HDMI1 (JSON body, seconds=8, closed by itself) and HDMI2 (form body), /status followed, /stop closed it; it was listening again after an in-place update, and switching it off closed the port. Restarting the TV was not tried. The JVM tests cover the playlist reader, addresses and inputs, hiding camera passwords, the window's place, and network control's requests and token.

Origin

Written as a Clean Your TV add-on and kept here as its own project, listed in that repo's showcase.

License

This project is licensed under the MIT License.

See the LICENSE file for details. Full license text: https://opensource.org/licenses/MIT

Contributing

Pull requests are welcome. Open an issue first to discuss ideas or report bugs. See CONTRIBUTING.md and the Code of Conduct.

Related Projects

Acknowledgments

Playback by AndroidX Media3 (Apache License 2.0). The screenshot's test video is Big Buck Bunny, © Blender Foundation, CC BY 3.0.
Android™, Android TV™ and Google TV™ are trademarks of Google LLC, used here solely to describe compatibility. All other trademarks are the property of their respective owners.

About

Picture in picture for Android TV: your own camera (a baby monitor, the front door) or stream plays in a corner over any app. A Clean Your TV add-on.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages