DLNA media server

dlnad puts a folder of films, series and music on your TV. It is a DLNA / UPnP AV media server: the TV finds it on the network, browses the folder and plays the files, seeking as it likes. There is no library to build or rescan. Every browse lists the directory as it is now, so a file you drop in shows up the next time the TV looks.

It is a Bus citizen written in Mix (mix --serve), so the same service answers the TV over HTTP and SSDP and answers people and agents on the Bus.

Requires Mix 0.118.0 or later for offset file reads and discovery sockets.

What it serves

  • One media directory, browsed live. Folders become browse levels; dotfiles are hidden; files are shown when their extension is a known video, audio or image type.
  • Subtitles: a video's sibling .srt with the same name is offered to the renderer (the sec:CaptionInfoEx element and CaptionInfo.sec header that Samsung and LG sets read).
  • Byte-range streaming, so renderers seek and resume. There is no transcoding: the files go out as they are, which suits renderers that play MKV, MP4 and HEVC natively.

Discovery

Renderers find a server by SSDP on UDP 239.255.255.250:1900. dlnad joins that group on the interface of its listen address, answers searches, and sends ssdp:alive adverts every announce_s seconds, with ssdp:byebye before an identity change. On a host with several networks, listen is what puts the server on the network your TV is on.

Some TVs never join the multicast group and miss multicast while they doze, so they forget a server between adverts. List their addresses in renderers and every advert is also sent to them unicast, which their Wi-Fi delivers reliably.

Configuration

<etc>/dlnad/dlnad.conf.mix (normally /etc/mixos/dlnad/dlnad.conf.mix):

{
  media_root: "/srv/media",
  listen: "192.0.2.30",
  port: 8200,
  name: "MixOS Media",
  ssdp: true,
  renderers: ["192.0.2.212"],
  announce_s: 60,
  max_age: 1800
}
KeyDefaultMeaning
media_root(required)the directory to serve
listen(required)the LAN IPv4 renderers reach; HTTP binds it, SSDP joins on it
port8200HTTP port
nameMixOS Mediafriendly name shown on the TV
ssdptrueannounce and answer searches
renderers[]IPv4 addresses that also get every advert unicast
announce_s60seconds between alive adverts
max_age1800how long a renderer may cache an advert
max_conns64HTTP admission limit; evict the oldest idle connection when full, or close the newcomer if all are busy

Timers and limits (announce_s, max_age, max_conns) must be positive numbers; max_conns must be an integer. Invalid values refuse startup and name the key. Renderer history retains the 256 most recently seen peers.

The server's identity (its UPnP uuid and friendly name) lives in <var>/dlnad/identity.conf.mix and survives restarts. A configuration change needs a service restart.

Bus commands

CommandBodyReply
dlnad.status—identity, configuration, SSDP state, counters
dlnad.list{id, start, count}one container's children as data (id "0" is the top)
dlnad.renderers—clients seen over HTTP or searching by SSDP, with their user agent
dlnad.announce—sends the alive adverts now
dlnad.identity.rotate{keep_name}byebye, a new uuid (and name), alive
send dlnad dlnad.status
print($reply.name .. " at " .. $reply.location)

When the TV shows stale content or an old server, rotate the identity. Many TVs cache a server's browse tree under its uuid; a new uuid is a server they have never seen. With the default the name changes too (MixOS Media 1A2B), so the fresh entry is easy to pick; {keep_name: true} keeps the name, and the TV may need a restart to notice.

Running it

The unit is dlnad.service (a dynamic user, state under /var/lib/mixos/dlnad). The media directory must be readable by that user: world-readable, or add its group with a drop-in (SupplementaryGroups=). Install the component with mix tools/component_install.mix dlnad.

Limits

  • One media directory, IPv4 only, HTTP (no TLS: DLNA renderers do not speak it).
  • No transcoding, thumbnails or metadata scraping; titles are file names.
  • No GENA notifications: subscriptions are acknowledged but never fired. BrowseDirectChildren's update id is the browsed container's own mtime; BrowseMetadata and GetSystemUpdateID use the root directory's mtime.
  • Sorting is fixed ascending (folders first, then files, each by name). SortCriteria is ignored and GetSortCapabilities returns empty SortCaps.
  • The SSDP and HTTP event commands (dlnad.http.*, dlnad.ssdp) are visible on the Bus like any on handler; they act only on sockets the running service owns.

This page as Markdown · This page describes the current main branch of MixOS.