About addozhang/leancast
addozhang/leancast is an open-source project on GitHub, mainly written in Kotlin. Ad-free DLNA / UPnP cast receiver for Android TV · 极简无广告投屏接收器 It currently holds 113 stars and 8 forks with 0 open issues, and was last pushed on 2026-10-29 (repository created 2026-10-28).
Project Overview
Git Homed tracks it on the Today's Trending board, currently at rank #68 with 0 new stars today.
GitHub Repository Details
README
LeanCast
English | 简体中文
A minimal, ad-free DLNA/UPnP cast receiver for Android TV. Open any video app with a cast button (Bilibili, iQiyi, Tencent Video, Youku, Quark, …), pick this receiver, and the video plays fullscreen with hardware decoding — no ads, no accounts, no cloud.
Phone app --SSDP--> discovers Phone app --SOAP SetAVTransportURI--> LeanCast
|
ExoPlayer + MediaCodec HW decode
Highlights
- Standard UPnP AV
MediaRenderer:1— SSDP discovery, AVTransport /
LastChange on subscribe, then a notification per state change).
- Hardware decoding via Media3/ExoPlayer: HLS (
.m3u8), progressive MP4,
- TV-native controls & OSD: OK = pause/resume, ◀ ▶ = seek with an
- Lean & private: lighter than the stock TV launcher (see below); zero
armeabi-v7a, arm64-v8a, x86, x86_64.
- Nothing hardcoded: the cast name defaults to
LeanCastand is
- Tested on an Amazon Fire TV (Fire OS 7 / Android 9, 32-bit MTK SoC) with
minSdk 21 / targetSdk 28.
Build
Requirements: JDK 17+, Android SDK (platform 34). Then:
./gradlew assembleDebug
app/build/outputs/apk/debug/app-debug.apk
Install & use
adb connect :5555
adb install -r app/build/outputs/apk/debug/app-debug.apk
adb shell am start -n io.leancast/io.leancast.app.MainActivity
Launch the app once to start the receiver service; it then appears in every DLNA sender's device list under the receiver name (rename it in the app). Optional: enable Start on boot in the app.
Supported devices
LeanCast is a pure-Kotlin APK with no native libraries and minSdk 21
(Android 5.0, 2014), so a single artifact covers armeabi-v7a, arm64-v8a,
x86 and x86_64. Decoding uses each device's platform MediaCodec, so codec
coverage follows the device: H.264 everywhere, HEVC/4K on 2016+ TVs, AV1 only
on newer boxes.
| Tier | Devices | Notes | | :--- | :--- | :--- | | ✅ Verified | Amazon Fire TV / Fire TV Stick (Fire OS 6/7) | Primary test device: Fire OS 7.7, 32-bit MTK SoC | | ✅ Expected | Certified Android TV / Google TV (Sony, TCL, Philips, Chromecast w/ Google TV), NVIDIA Shield, Amlogic / Rockchip boxes, Xiaomi international boxes | Sideload and go | | ⚠️ Experimental | CN smart TVs with sideloading (MIUI TV / PatchWall, Coocaa, Hisense Android models), Tmall boxes (YunOS) | Enable "unknown sources"; report results via the compatibility issue template | | ❌ Unsupported | Samsung (Tizen), LG (webOS), Huawei Vision (HarmonyOS), non-smart TVs | No Android app runtime — use any TV box from the ✅ tiers |
Notes:
- Phone "system cast" (Miracast) is out of scope — LeanCast is a DLNA/UPnP
[TV] button inside video apps.
- Tested an unlisted device? Open a Device compatibility report issue and
Resource usage
Measured on the primary test device (Fire TV, 1.6 GB RAM); idle = receiver waiting on the dashboard:
| State | RAM (PSS) | CPU | Storage | | :--- | :--- | :--- | :--- | | Idle / waiting | 63–72 MB (≈ 33 MB private) | ≈ 0% | 5 MB APK + < 0.1 MB data | | Casting 1080p | ≈ 74 MB | ~4–5% of total (decode is hardware) | — |
Lighter than the stock Fire TV launcher (83 MB PSS) on the same device.
Releases & CI
Two GitHub Actions workflows ship with the repo:
- CI (
.github/workflows/ci.yml) — runs domain unit tests + debug build on
main and every PR; uploads the debug APK as an artifact.
- Release (
.github/workflows/release.yml) — triggered by pushing av*
LeanCast-.apk.
To publish a release:
git tag v0.2.0
git push origin v0.2.0
Release signing
By default the release APK falls back to the debug keystore (fine for personal sideloading). For a stable release signature across builds, add these repository secrets (Settings → Secrets and variables → Actions):
| Secret | Value |
| :--- | :--- |
| KEYSTORE_BASE64 | base64 -i release.keystore \| pbcopy (or base64 -w0 on Linux) |
| KEYSTORE_PASSWORD | Keystore password |
| KEY_ALIAS | Key alias |
| KEY_PASSWORD | Key password |
The workflow decodes the keystore to ci-release.keystore (git-ignored) and
signs with it. Generate a keystore once with:
keytool -genkeypair -v -keystore release.keystore -alias leancast \
-keyalg RSA -keysize 2048 -validity 10000
Project layout
domain/ Pure-Kotlin protocol core (JVM-unit-tested, 90 tests)
ssdp/ M-SEARCH parsing, target matching, USN, alive/byebye, MX delays
upnp/ Device description + SCPDs, SOAP parser (prefix-tolerant DIDL),
response envelopes & faults, H:MM:SS time math, SOAP router
cast/ AVTransport state machine, player port, volume math
app/ Android shell (thin adapters, E2E-verified on device)
ControlServer Minimal HTTP/1.1 server: /desc.xml, /scpd/, /ctl/, /evt/*
SsdpAgent Multicast UDP listener + advertiser (MulticastLock held)
ExoPlayerPort PlayerPort adapter (main-thread marshalled, cached position)
ReceiverService Foreground service assembling everything
tools/
e2e_control_point.py Scripted UPnP control point for device E2E runs
openspec/ Spec-driven development artifacts (proposal/specs/design/tasks)
Development follows OpenSpec (openspec/) with strict TDD: every spec
scenario maps to unit tests in domain; run them with ./gradlew :domain:test.
End-to-end verification drives a real device from a dev box:
python3 tools/e2e_control_point.py http:///video.mp4
License
Apache-2.0. Dependencies: Media3 (Apache-2.0).