mini-mdr is a tray-only, cross-platform UPnP/DLNA Digital Media Renderer
(DMR). It receives playback commands from a phone or desktop control point and
plays audio or video through a replaceable player backend.
The default player backend is an external mpv process
controlled through mpv's JSON IPC interface.
- Windows, macOS, and Linux target design
- Audio and video playback URLs
- UPnP/DLNA MediaRenderer device description
- SSDP discovery (
M-SEARCHresponses, periodicssdp:alive,ssdp:byebyeon stop) ConnectionManagerwith protocol info and connection queriesAVTransportwithSetAVTransportURI,Play,Pause,Stop,Seek(REL_TIME),GetTransportInfo,GetPositionInfo,GetMediaInfoRenderingControlwith master-channel volume and mute- GENA event subscriptions with renewal, expiry, and
LastChangenotifications - Pluggable
PlayerBackendabstraction - Default
mpvbackend over JSON IPC (named pipe on Windows, Unix socket elsewhere); the mpv process starts lazily on first media load - Optional
VLCbackend over HTTP interface - System-tray-only application control with a dynamic Start/Stop Cast label
- Local settings server with an editable form that persists configuration
- Media history tracking with configurable max entries (persisted to disk)
- Signal handling for clean shutdown (SIGINT, SIGHUP, SIGTERM on Unix)
- Auto-start cast on launch
- Embedded 32x32 RGBA8 tray icon with no runtime image decoding
- i18n support: English and Chinese, auto-detected from system locale
The tray menu has the following structure:
Start Cast/Stop Cast(checkbox reflecting current Cast state)More> (submenu)Open SettingsOpen Directory(opens the config/log directory in the file manager)
Quit
DMC: phone / desktop controller
│ SSDP, SOAP, GENA
▼
mini-mdr
├── SSDP discovery
├── UPnP HTTP and SOAP services
├── renderer state
├── player backend abstraction
└── system tray
│
▼
mpv JSON IPC
│
├── video output
└── audio output
The DMC sends control commands to mini-mdr. The DMR then gives the media URL
to the player backend. The player, not the DMC, retrieves the actual media from
the DMS over HTTP.
DMC ── SOAP control ──> mini-mdr ── IPC ──> mpv
DMS ── HTTP media ───────────────────────> mpv
- Rust stable toolchain
- A desktop environment with a system tray
mpvinstalled and available inPATH, unless a custom path is configured- A UPnP/DLNA control point on the same local network
On Windows, use the MSVC Rust toolchain. On Linux, the desktop environment must provide a tray-compatible notification area or StatusNotifier host. Headless environments may run the non-tray protocol components, but tray creation is expected to fail gracefully.
Install mpv, then run:
cargo run
The application starts with Cast services and the settings HTTP server enabled.
The settings server binds to 127.0.0.1:7878 (or an ephemeral fallback port if
busy) and opens the page with the system default browser when Open Settings is
selected.
Start Cast starts the UPnP HTTP server and SSDP discovery service. It also
creates the configured player backend. Once running, compatible DMC clients can
discover and control the renderer.
Stop Cast stops the current player, releases the player backend, and stops the
Cast services. It does not stop the settings server if that server was already
started.
The settings server starts automatically on launch:
Application startup -> bind 127.0.0.1:7878
Port conflict -> select an ephemeral fallback port
Open Settings click -> open the page in the default browser
The server listens only on loopback and opens the page with the system default browser. Current settings include the device name, player backend, mpv path, and vlc path.
Quit stops Cast services, stops the player process, closes the settings
server when present, and terminates the application.
The default configuration is:
[device]
name = "mini-mdr(<hostname>)" # hostname from $HOSTNAME or $HOST
[player]
backend = "mpv"
mpv_path = "mpv"
vlc_path = "vlc"
[settings]
port = 7878
max_history = 200Device name includes the system hostname by default (e.g., mini-mdr(laptop)).
History entries are persisted to history.json in the same config directory.
The configuration is loaded from the platform-specific user configuration
directory provided by the directories crate. If no file exists, these
defaults are used.
The protocol layer depends on the PlayerBackend trait rather than directly on
mpv:
PlayerBackend
├── MpvBackend (default)
└── VlcBackend
The interface currently covers loading, play, pause, stop, seek, volume, mute,
status queries, and protocol info reporting. Future backends such as GStreamer
or FFmpeg should implement this trait and be registered in
player::create_backend.
The current plugin model is compile-time extensibility through Rust implementations selected by configuration. Runtime loading of third-party DLLs or shared libraries is not implemented.
Device and service descriptions are maintained as standalone files under
resources/ and embedded into the executable at compile time with
include_str!. Deployment therefore does not require separate XML files.
The editable tray artwork is resources/icon.png. Before committing an icon
change, convert it offline to resources/icon.rgba: 32x32, row-major,
non-premultiplied RGBA8 (4096 bytes). The executable embeds the raw bytes with
include_bytes! and passes them directly to ldtray; no image decoder or
startup transformation is involved.
The current implementation provides a deliberately small protocol surface:
device.xmlplus one SCPD file per service, embedded at compile time- SSDP discovery with standard search targets and lifecycle announcements
- SOAP control for transport, position/media queries, seek, volume, and mute
- Standard UPnP error responses with error codes
- GENA
SUBSCRIBE/UNSUBSCRIBEwith SID renewal, timeout clamping, expiry cleanup, andLastChangenotifications to subscriber callbacks
The implementation is not yet a complete DLNA certification implementation. In
particular, requests are served sequentially, track metadata is minimal
(no DIDLLite), and only instance 0 exists.
Format and verify the project with:
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
Before testing playback, verify that mpv is available:
mpv --version
Useful manual checks:
- Start the application and confirm the settings port is listening on
127.0.0.1. - Click
Open Settingsand confirm a browser opens on127.0.0.1. - Click
Start Castand send an SSDPM-SEARCHrequest. - Fetch the returned
device.xmlURL. - Use a compatible DMC to send
SetAVTransportURIandPlay. - Stop Cast and confirm the UPnP and SSDP services are released.
This is an early MVP under active development. Current status:
- Core UPnP/DLNA protocol implemented (AVTransport, ConnectionManager, RenderingControl)
- SSDP discovery and lifecycle management working
- mpv backend with JSON IPC control
- System tray integration with start/stop cast, settings, and quit
- Settings page with configuration editing and media history
- History persistence to disk with configurable max entries
- Signal handling for clean shutdown on Unix
- AUR packaging for Arch Linux
Priority follow-up work: concurrent request handling, persistent per-install UPnP UDN, DIDLLite metadata, and integration tests.