Racket UPnP module
This directory contains a small, layered UPnP control-point implementation. The public interfaces deliberately hide SSDP, XML, SCPD and SOAP details unless the generic service escape hatch is used.
Module layout
upnp/main.rkt generic public interface
upnp/didl-lite.rkt DIDL-Lite metadata generation
upnp/media-renderer.rkt high-level renderer operations
upnp/media-server.rkt high-level MediaServer browser
upnp/services/av-transport.rkt playback transport
upnp/services/rendering-control.rkt volume and mute
upnp/services/connection-manager.rkt protocol and connection information
upnp/services/content-directory.rkt media-server browse and search
upnp/ssdp.rkt low-level SSDP discovery
upnp/device.rkt device-description parsing
upnp/service.rkt SCPD inspection and SOAP calls
The collection can be required as upnp when the package root is on Racket's
collection path.
Generic device discovery
#lang racket/base
(require upnp)
(define devices
(query-upnp-devices '(media-renderer scanner)
#:interface "10.7.3.118"
#:dns? #t))
(for ([device (in-list devices)])
(printf "~a: ~a\n"
(upnp-device-kind device)
(upnp-device-name device)))
Known friendly device kinds are returned by:
(upnp-device-kinds)
Without a kind, all described UPnP devices are returned:
(query-upnp-devices)
Unknown vendor-specific device types are retained and classified as
'unknown; their original URN remains available through upnp-device-type.
Service inspection
(define renderer (car (query-upnp-devices 'media-renderer)))
(for ([service (in-list (upnp-device-services renderer))])
(printf "~a: ~a\n"
(upnp-service-kind service)
(upnp-service-actions service)))
upnp-service-call is the generic escape hatch for standard services that do
not yet have a typed module and for manufacturer-specific services.
One module per known service
(require upnp/services/av-transport
upnp/services/rendering-control)
(define transport (device-av-transport renderer))
(define rendering (device-rendering-control renderer))
(av-transport-set-uri!
transport
"http://10.7.3.118:8080/media/test.flac")
(av-transport-play! transport)
(rendering-control-set-volume! rendering 30)
The currently implemented typed service modules are:
upnp/services/av-transportupnp/services/rendering-controlupnp/services/connection-managerupnp/services/content-directory
A typed service value is still the generic upnp-service value. No extra
wrapper objects or public constructors are needed.
Media-server browser
The higher media-server layer parses ContentDirectory DIDL-Lite into ordinary containers, items and resources:
(require upnp/media-server)
(define synology
(car (query-media-servers #:interface "10.7.3.118")))
(define root (media-server-root synology))
(define music
(findf (lambda (entry)
(and (media-container? entry)
(string=? (media-entry-title entry) "Muziek")))
root))
(define entries (media-container-children synology music))
(for ([entry (in-list entries)])
(printf "~a: ~a\n"
(if (media-container? entry) 'container 'item)
(media-entry-title entry)))
A media item can advertise several resources. Their URLs are available with
media-item-resources and media-resource-uri. Containers are followed one
level at a time; the published hierarchy can be a logical artist/album/genre
view rather than the physical filesystem.
Media-renderer convenience layer
Normal playback code need not mention AVTransport or SOAP:
(require upnp
upnp/media-renderer)
(define files
(start-media-file-server
"http://10.7.3.118:8080/media/"
#:listen-ip "10.7.3.118"))
(define uri
(media-file-server-publish!
files
"C:/muziek/test.flac"
"test.flac"))
(define denon
(car (query-media-renderers
#:interface "10.7.3.118"
#:dns? #t)))
(media-renderer-play-uri!
denon
uri
#:metadata
(media-file-server-didl-lite
files
uri
#:title "Test track"
#:duration 639))
(media-renderer-seek! denon 120)
(media-renderer-set-volume! denon 30)
query-media-renderers returns all UPnP MediaRenderers by default. Add
#:dlna-only? #t to retain only devices advertising X_DLNADOC.
For local files, media-file-server-publish! supplies a byte-range-capable
HTTP URL. media-file-server-didl-lite generates matching DIDL-Lite metadata,
including MIME type, file size, duration, and DLNA.ORG_OP=01. Some renderers,
including certain Denon models, only advertise Seek after receiving this
metadata. Generate and pass matching metadata for both the current URI and a
preloaded next URI.
Documentation and tests
The Scribble manual is in upnp/scribblings/upnp.scrbl.
The test file upnp/tests/upnp-test.rkt uses a local mock HTTP/SOAP server and
covers service discovery from SCPD, SOAP calls, SOAP faults, AVTransport,
RenderingControl, ConnectionManager, ContentDirectory and DIDL-Lite media
server browsing.