70template <
typename ClientType>
177 active ?
"true" :
"false");
189 static size_t write(Print& out,
void* ref) {
193 written += out.print(
"<TransportState val=\"");
194 written += out.print(self->getTransportState());
195 written += out.print(
"\"/>");
196 written += out.print(
"<CurrentTransportActions val=\"");
197 written += out.print(self->getCurrentTransportActions());
198 written += out.print(
"\"/>");
223 auto writer = [](Print& out,
void* ref) ->
size_t {
226 written += out.print(
"<Volume val=\"");
227 written += out.print(self->current_volume);
228 written += out.print(
"\" channel=\"Master\"/>");
247 auto writer = [](Print& out,
void* ref) ->
size_t {
250 written += out.print(
"<Mute val=\"");
251 written += out.print(self->isMuted() ? 1 : 0);
252 written += out.print(
"\" channel=\"Master\"/>");
295 bool play(
const char* urlStr) {
296 if (urlStr ==
nullptr)
return false;
307 auto writer = [](Print& out,
void* ref) ->
size_t {
310 written += out.print(
"<CurrentTrack val=\"1\"/>\n");
311 written += out.print(
"<CurrentTrackURI val=\"");
312 written += out.print(self->current_uri.c_str());
313 written += out.println(
"\"/>");
314 written += out.print(
"<CurrentTrackMetaData val=\"");
316 written += esc.print(self->current_uri_metadata.c_str());
317 written += out.println(
"\"/>");
318 written += out.print(
"<TransportState val=\"");
319 written += out.print(self->transport_state.c_str());
320 written += out.print(
"\"/>");
321 written += out.print(
"<CurrentTransportActions val=\"");
322 written += out.print(self->transport_actions.c_str());
323 written += out.print(
"\"/>");
333 if (urlStr ==
nullptr)
return false;
339 auto writer = [](Print& out,
void* ref) ->
size_t {
342 written += out.print(
"<AVTransportURI val=\"");
343 written += out.print(self->current_uri.c_str());
344 written += out.println(
"\"/>");
346 written += out.print(
"<AVTransportURIMetaData val=\"");
348 written += esc.print(self->current_uri_metadata.c_str());
349 written += out.println(
"\"/>");
351 written += out.print(
"<NumberOfTracks val=\"1\"/>\n");
367 auto writer = [](Print& out,
void* ref) ->
size_t {
370 written += out.print(
"<TransportState val=\"");
371 written += out.print(self->getTransportState());
372 written += out.print(
"\"/>");
373 written += out.print(
"<CurrentTransportActions val=\"");
374 written += out.print(self->getCurrentTransportActions());
375 written += out.print(
"\"/>");
398 auto writer = [](Print& out,
void* ref) ->
size_t {
401 written += out.print(
"<TransportState val=\"");
402 written += out.print(self->transport_state.c_str());
403 written += out.print(
"\"/>");
404 written += out.print(
"<CurrentPlayMode val=\"NORMAL\"/>");
405 written += out.print(
"<CurrentTrackURI val=\"\"/>");
406 written += out.print(
"<RelativeTimePosition val=\"00:00:00\"/>");
407 written += out.print(
"<CurrentTransportActions val=\"Play\"/>");
416 return static_cast<size_t>(posMs / 1000);
421 auto writer = [](Print& out,
void* ref) ->
size_t {
425 int h =
static_cast<int>(self->getRelativeTimePositionSec() / 3600);
427 static_cast<int>((self->getRelativeTimePositionSec() % 3600) / 60);
428 int s =
static_cast<int>(self->getRelativeTimePositionSec() % 60);
429 written += out.print(
"<RelativeTimePosition val=\"");
431 if (h < 10) written += out.print(
'0');
432 written += out.print(h);
433 written += out.print(
':');
434 if (m < 10) written += out.print(
'0');
435 written += out.print(m);
436 written += out.print(
':');
437 if (s < 10) written += out.print(
'0');
438 written += out.print(s);
439 written += out.print(
"\"/>");
448 return "SetAVTransportURI";
460 for (
size_t i = 0; i <
rules.size(); ++i) {
462 rules[i].handler = handler;
466 rules.push_back({suffix, handler});
485 auto writer = [](Print& out,
void* ref) ->
size_t {
488 result += out.print(
"<SourceProtocolInfo val=\"");
489 result += out.print(
StrView(self->getSourceProtocols()).c_str());
490 result += out.println(
"\"/>");
491 result += out.print(
"<SinkProtocolInfo val=\"");
492 result += out.print(
StrView(self->getSinkProtocols()).c_str());
493 result += out.println(
"\"/>");
543 const char*
st =
"urn:schemas-upnp-org:device:MediaRenderer:1";
544 const char*
usn =
"uuid:09349455-2941-4cf7-9847-1dd5ab210e97";
572 std::function<
size_t(Print&,
void*)> changeWriter) {
580 auto writer = [](Print& out,
void* ref) ->
size_t {
584 written += outf.
printf(
"<TransportState val=\"%s\"/>",
585 self->transport_state.c_str());
586 if (!self->current_uri.isEmpty()) {
587 written += outf.printf(
"<CurrentTrackURI val=\"%s\"/>",
588 self->current_uri.c_str());
590 written += outf.printf(
591 "<RelativeTimePosition val=\"%02d:%02d:%02d\"/>",
592 static_cast<int>(self->getRelativeTimePositionSec() / 3600),
593 static_cast<int>((self->getRelativeTimePositionSec() % 3600) / 60),
594 static_cast<int>(self->getRelativeTimePositionSec() % 60));
595 written += outf.printf(
"<CurrentTransportActions val=\"%s\"/>",
596 self->getCurrentTransportActions());
604 auto writer = [](Print& out,
void* ref) ->
size_t {
608 written += outf.
printf(
"<Volume val=\"%d\" channel=\"Master\"/>", self->current_volume);
609 written += out.println();
610 written += outf.printf(
"<Mute val=\"%d\" channel=\"Master\"/>", self->isMuted() ? 1 : 0);
611 written += out.println();
619 auto writer = [](Print& out,
void* ref) ->
size_t {
622 return printer.
printf(
"<CurrentConnectionIDs>%s</CurrentConnectionIDs>");
642 char mimeBuf[256] = {0};
644 didl,
"<res",
"protocolInfo=", 3, mimeBuf,
sizeof(mimeBuf))) {
676 [](Print& out,
void* ref) ->
size_t {
685 "transportDescrCB: renderer instance not found for %s",
697 [](Print& out,
void* ref) ->
size_t {
706 "connmgrDescrCB: renderer instance not found for %s",
718 [](Print& out,
void* ref) ->
size_t {
727 "controlDescrCB: renderer instance not found for %s",
756 if (p_device ==
nullptr)
return nullptr;
762 const char* requestPath,
764 bool is_subscribe =
false;
768 assert(self !=
nullptr);
769 StrView request_path_str(requestPath);
770 if (request_path_str.
contains(
"/AVT/"))
772 else if (request_path_str.
contains(
"/RC/"))
774 else if (request_path_str.
contains(
"/CM/"))
783 avt.
setup(
"urn:schemas-upnp-org:service:AVTransport:1",
784 "urn:upnp-org:serviceId:AVTransport",
"/AVT/service.xml",
795 cm.
setup(
"urn:schemas-upnp-org:service:ConnectionManager:1",
796 "urn:upnp-org:serviceId:ConnectionManager",
"/CM/service.xml",
807 rc.
setup(
"urn:schemas-upnp-org:service:RenderingControl:1",
808 "urn:upnp-org:serviceId:RenderingControl",
"/RC/service.xml",
838 if (action_str.isEmpty()) {
844 for (
const auto& rule :
rules) {
845 if (action_str.endsWith(rule.suffix)) {
846 return rule.handler(
this, action, server, client);
860 [](Print& out,
void* ref) ->
size_t {
873 [](Print& out,
void* ref) ->
size_t {
875 out,
"ListPresetsResponse",
"AVTransport",
876 [](Print& o,
void* ) ->
size_t {
878 written += o.print(
"<CurrentPresetNameList>FactoryDefault</CurrentPresetNameList>");
891 [](Print& out,
void* ref) ->
size_t {
905 [](Print& out,
void* ref) ->
size_t {
918 [](Print& out,
void* ref) ->
size_t {
922 out,
"GetCurrentTransportActionsResponse",
"AVTransport",
923 [](Print& o,
void* innerRef) ->
size_t {
926 written += o.print(
"<Actions>");
927 written += o.print(self->getCurrentTransportActions());
928 written += o.print(
"</Actions>");
942 [](Print& out,
void* ref) ->
size_t {
945 out, self->getSourceProtocols(), self->getSinkProtocols());
956 [](Print& out,
void* ref) ->
size_t {
959 out, self->getConnectionID());
970 [](Print& out,
void* ref) ->
size_t {
973 out, self->getSinkProtocols(), self->getConnectionID(),
"Input");
984 [](Print& out,
void* ref) ->
size_t {
986 out,
"GetDeviceCapabilitiesResponse",
"AVTransport",
987 [](Print& o,
void* innerRef) ->
size_t {
990 written += o.print(
"<PlayMedia>");
992 StrView(self->getPossiblePlaybackStorageMedia()).
c_str());
993 written += o.print(
"</PlayMedia>");
994 written += o.print(
"<RecMedia>");
996 StrView(self->getPossibleRecordStorageMedia()).
c_str());
997 written += o.print(
"</RecMedia>");
998 written += o.print(
"<RecQualityModes>");
1000 StrView(self->getPossibleRecordQualityModes()).
c_str());
1001 written += o.print(
"</RecQualityModes>");
1006 200,
nullptr,
this);
1015 [](Print& out,
void* ref) ->
size_t {
1017 out,
"GetTransportInfoResponse",
"AVTransport",
1018 [](Print& o,
void* innerRef) ->
size_t {
1022 written += o.print(
"<CurrentTransportState>");
1023 written += o.print(self->getTransportState());
1024 written += o.print(
"</CurrentTransportState>");
1028 "<CurrentTransportStatus>OK</CurrentTransportStatus>");
1031 written += o.print(
"<CurrentSpeed>1</CurrentSpeed>");
1036 200,
nullptr,
this);
1045 [](Print& out,
void* ref) ->
size_t {
1047 out,
"GetPositionInfoResponse",
"AVTransport",
1048 [](Print& o,
void* innerRef) ->
size_t {
1051 const bool hasUri = self->getCurrentUri() && *self->getCurrentUri();
1054 written += o.print(
"<Track>");
1055 written += o.print(hasUri ? 1 : 0);
1056 written += o.print(
"</Track>");
1059 written += o.print(
"<TrackDuration>00:00:00</TrackDuration>");
1062 written += o.print(
"<TrackMetaData>");
1064 written += esc.print(self->getCurrentUriMetadata());
1065 written += o.print(
"</TrackMetaData>");
1068 written += o.print(
"<TrackURI>");
1069 written += o.print(
StrView(self->getCurrentUri()).
c_str());
1070 written += o.print(
"</TrackURI>");
1073 unsigned long secs = self->getRelativeTimePositionSec();
1074 int h =
static_cast<int>(secs / 3600);
1075 int m =
static_cast<int>((secs % 3600) / 60);
1076 int s =
static_cast<int>(secs % 60);
1077 written += o.print(
"<RelTime>");
1078 if (h < 10) written += o.print(
'0');
1079 written += o.print(h);
1080 written += o.print(
':');
1081 if (m < 10) written += o.print(
'0');
1082 written += o.print(m);
1083 written += o.print(
':');
1084 if (s < 10) written += o.print(
'0');
1085 written += o.print(s);
1086 written += o.print(
"</RelTime>");
1089 written += o.print(
"<AbsTime>00:00:00</AbsTime>");
1092 written += o.print(
"<RelCount>2147483647</RelCount>");
1093 written += o.print(
"<AbsCount>2147483647</AbsCount>");
1099 200,
nullptr,
this);
1108 [](Print& out,
void* ref) ->
size_t {
1110 out,
"GetTransportSettingsResponse",
"AVTransport",
1111 [](Print& o,
void* innerRef) ->
size_t {
1115 written += o.print(
"<PlayMode>");
1116 written += o.print(
StrView(self->getPlayMode()).
c_str());
1117 written += o.print(
"</PlayMode>");
1121 written += o.print(
"<RecQualityMode>");
1122 written += o.print(
"NOT_IMPLEMENTED");
1123 written += o.print(
"</RecQualityMode>");
1128 200,
nullptr,
this);
1136 [](Print& out,
void* ref) ->
size_t {
1138 out,
"GetMediaInfoResponse",
"AVTransport",
1139 [](Print& o,
void* innerRef) ->
size_t {
1144 written += o.print(
"<NrTracks>");
1146 self->getCurrentUri() && *self->getCurrentUri() ? 1 : 0);
1147 written += o.print(
"</NrTracks>");
1150 written += o.print(
"<MediaDuration></MediaDuration>");
1153 written += o.print(
"<CurrentURI>");
1154 written += o.print(
StrView(self->getCurrentUri()).
c_str());
1155 written += o.print(
"</CurrentURI>");
1156 written += o.print(
"<CurrentURIMetaData>");
1158 written += esc.print(self->getCurrentUriMetadata());
1159 written += o.print(
"</CurrentURIMetaData>");
1162 written += o.print(
"<NextURI>NOT_IMPLEMENTED</NextURI>");
1164 "<NextURIMetaData>NOT_IMPLEMENTED</NextURIMetaData>");
1168 written += o.print(
"<PlayMedium>");
1170 StrView(self->getPossiblePlaybackStorageMedia()).
c_str());
1171 written += o.print(
"</PlayMedium>");
1173 written += o.print(
"<RecordMedium>");
1175 StrView(self->getPossibleRecordStorageMedia()).
c_str());
1176 written += o.print(
"</RecordMedium>");
1179 o.print(
"<WriteStatus>NOT_IMPLEMENTED</WriteStatus>");
1185 200,
nullptr,
this);
1198 [](Print& out,
void* ref) ->
size_t {
1203 200,
nullptr,
this);
1207 "SetAVTransportURI called with invalid SOAP payload");
1219 [](Print& out,
void* ref) ->
size_t {
1222 "RenderingControl");
1224 200,
nullptr,
this);
1234 [](Print& out,
void* ref) ->
size_t {
1237 "RenderingControl");
1239 200,
nullptr,
this);
1246 [](Print& out,
void* ref) ->
size_t {
1248 out,
"GetMuteResponse",
"RenderingControl",
1249 [](Print& o,
void* innerRef) ->
size_t {
1252 written += o.print(
"<CurrentMute>");
1253 written += o.print(self->isMuted() ? 1 : 0);
1254 written += o.print(
"</CurrentMute>");
1259 200,
nullptr,
this);
1266 [](Print& out,
void* ref) ->
size_t {
1268 out,
"GetVolumeResponse",
"RenderingControl",
1269 [](Print& o,
void* innerRef) ->
size_t {
1274 written += o.print(
"<CurrentVolume>");
1275 written += o.print((
int)self->getVolume());
1276 written += o.print(
"</CurrentVolume>");
1281 200,
nullptr,
this);
1338 rules.push_back({
"GetCurrentConnectionIDs",
1342 action, server, client);
1344 rules.push_back({
"GetCurrentConnectionInfo",
1348 action, server, client);
Represents a request to invoke a remote DLNA service action.
Definition: Action.h:104
Str & getActionStr()
Definition: Action.h:159
const char * getArgumentValue(const char *name)
Definition: Action.h:128
int getArgumentIntValue(const char *name)
Definition: Action.h:146
const char * getAction()
Definition: Action.h:157
Abstract DLNA Descriptor Generation.
Definition: DLNADescr.h:9
virtual size_t printDescr(Print &out)=0
Device Attributes and generation of XML using urn:schemas-upnp-org:device-1-0. We could just return a...
Definition: DLNADeviceInfo.h:28
void setDeviceType(const char *st)
Definition: DLNADeviceInfo.h:84
void addService(DLNAServiceInfo s)
Adds a service definition.
Definition: DLNADeviceInfo.h:187
void setModelName(const char *name)
Definition: DLNADeviceInfo.h:168
void setSerialNumber(const char *sn)
Definition: DLNADeviceInfo.h:172
void setManufacturer(const char *man)
Definition: DLNADeviceInfo.h:162
void setUDN(const char *id)
Define the udn uuid.
Definition: DLNADeviceInfo.h:89
void setFriendlyName(const char *name)
Definition: DLNADeviceInfo.h:160
bool is_active
Definition: DLNADeviceInfo.h:290
void setModelNumber(const char *number)
Definition: DLNADeviceInfo.h:170
void setBaseURL(const char *url)
Defines the base url.
Definition: DLNADeviceInfo.h:95
Setup of a Basic DLNA Device service. The device registers itself to the network and answers to the D...
Definition: DLNADevice.h:47
void end() override
Stops the processing and releases the resources.
Definition: DLNADevice.h:119
void setReference(void *ref) override
Sets a reference pointer that can be used to associate application.
Definition: DLNADevice.h:473
static size_t printReplyXML(Print &out, const char *replyName, const char *serviceId, std::function< size_t(Print &, void *)> valuesWriter=nullptr, void *ref=nullptr)
Builds a standard SOAP reply envelope.
Definition: DLNADevice.h:355
static void parseActionRequest(IHttpServer *server, IClientHandler &client, const char *requestPath, HttpRequestHandlerLine *hl, ActionRequest &action)
Parses the SOAP content of a DLNA action request.
Definition: DLNADevice.h:277
bool begin(DLNADeviceInfo &device, IUDPService &udp, IHttpServer &server) override
start the
Definition: DLNADevice.h:54
static size_t replyGetCurrentConnectionInfo(Print &out, const char *protocolInfo, const char *connectionID, const char *direction)
Definition: DLNADevice.h:418
bool loop(int loopAction=RUN_ALL) override
Definition: DLNADevice.h:142
static size_t replyGetProtocolInfo(Print &out, const char *source="", const char *sink="")
Definition: DLNADevice.h:385
static bool handleSubscription(IHttpServer *server, IClientHandler *client, const char *requestPath, HttpRequestHandlerLine *hl, bool &is_subscribe)
Static handler for SUBSCRIBE/UNSUBSCRIBE requests on service event URLs.
Definition: DLNADevice.h:448
static size_t replyGetCurrentConnectionIDs(Print &out, const char *ids)
Definition: DLNADevice.h:402
void setSubscriptionsActive(bool flag) override
Enable or disable subscription notifications: call before begin.
Definition: DLNADevice.h:263
bool isSubscriptionsActive() const override
Check if subscription notifications are active.
Definition: DLNADevice.h:272
void addChange(const char *serviceAbbrev, std::function< size_t(Print &, void *)> changeWriter, void *ref) override
Record a state variable change for subscription notifications.
Definition: DLNADevice.h:232
Attributes needed for the DLNA Service Definition.
Definition: DLNAServiceInfo.h:18
void setup(const char *type, const char *id, const char *scp, http_callback cbScp, const char *control, http_callback cbControl, const char *event, http_callback cbEvent)
Setup all relevant values.
Definition: DLNAServiceInfo.h:23
const char * subscription_namespace_abbrev
Definition: DLNAServiceInfo.h:53
Used to register and process callbacks.
Definition: HttpRequestHandlerLine.h:12
Definition: IHttpServer.h:19
virtual HttpRequestHeader & requestHeader()=0
virtual void replyNotFound()=0
virtual void reply(const char *contentType, Stream &inputStream, int size, int status=200, const char *msg=SUCCESS)=0
virtual void replyError(int err, const char *msg="Internal Server Error")=0
Abstract interface for DLNA device functionality.
Definition: IDevice.h:30
Abstract interface for HTTP server functionality.
Definition: IHttpServer.h:50
virtual void * getReference()=0
virtual void setReference(void *reference)=0
Abstract Interface for UDP API.
Definition: IUDPService.h:33
Printf support with output to Print. This class does not do any heap allocations!
Definition: Printf.h:12
size_t printf(const char *fmt,...)
Definition: Printf.h:16
A simple wrapper to provide string functions on char*. If the underlying char* is a const we do not a...
Definition: StrView.h:18
virtual const char * c_str()
provides the string value as const char*
Definition: StrView.h:376
virtual bool contains(const char *str)
checks if the string contains a substring
Definition: StrView.h:284
Heap-backed string utility used throughout tiny_dlna.
Definition: Str.h:27
bool isEmpty() const
True if empty.
Definition: Str.h:54
void set(const char *v)
Assign from C-string (nullptr -> empty)
Definition: Str.h:120
void clear()
Clear contents (size -> 0)
Definition: Str.h:93
const char * c_str() const
C-string pointer to internal buffer.
Definition: Str.h:88
Lightweight wrapper around std::vector with Arduino-friendly helpers and a pluggable allocator.
Definition: Vector.h:39
static bool extractAttributeToken(const char *xml, const char *tagName, const char *attrName, int n, char *outBuf, size_t bufSize)
Extract the nth (1-based) colon-separated token from an attribute value located on the first occurren...
Definition: XMLAttributeParser.h:132
static bool extractElementText(const char *xml, const char *elementName, Str &out)
Extract the (unescaped) text content of the first element with the given name.
Definition: XMLAttributeParser.h:173
#define DLNA_PROTOCOL_AUDIO
All possible audio protocols.
Definition: dlna_config.h:160
Definition: Allocator.h:13
MediaEvent
Events emitted by the MediaRenderer to notify the application will receive the event along with a ref...
Definition: DLNAMediaRenderer.h:33
@ RUN_ALL
Definition: IDevice.h:18
void(* http_callback)(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Definition: DLNAServiceInfo.h:11
Print wrapper that escapes & < > " ' while forwarding to an underlying Print. Returns the expanded ou...
Definition: EscapingPrint.h:9