69template <
typename ClientType>
176 active ?
"true" :
"false");
188 static size_t write(Print& out,
void* ref) {
192 written += out.print(
"<TransportState val=\"");
193 written += out.print(self->getTransportState());
194 written += out.print(
"\"/>");
195 written += out.print(
"<CurrentTransportActions val=\"");
196 written += out.print(self->getCurrentTransportActions());
197 written += out.print(
"\"/>");
222 auto writer = [](Print& out,
void* ref) ->
size_t {
225 written += out.print(
"<Volume val=\"");
226 written += out.print(self->current_volume);
227 written += out.print(
"\" channel=\"Master\"/>");
246 auto writer = [](Print& out,
void* ref) ->
size_t {
249 written += out.print(
"<Mute val=\"");
250 written += out.print(self->isMuted() ? 1 : 0);
251 written += out.print(
"\" channel=\"Master\"/>");
284 bool play(
const char* urlStr) {
285 if (urlStr ==
nullptr)
return false;
296 auto writer = [](Print& out,
void* ref) ->
size_t {
299 written += out.print(
"<CurrentTrack val=\"1\"/>\n");
300 written += out.print(
"<CurrentTrackURI val=\"");
301 written += out.print(self->current_uri.c_str());
302 written += out.println(
"\"/>");
303 written += out.print(
"<CurrentTrackMetadata val=\"");
304 written += out.print(self->current_uri_metadata.c_str());
305 written += out.println(
"\"/>");
306 written += out.print(
"<TransportState val=\"");
307 written += out.print(self->transport_state.c_str());
308 written += out.print(
"\"/>");
309 written += out.print(
"<CurrentTransportActions val=\"");
310 written += out.print(self->transport_actions.c_str());
311 written += out.print(
"\"/>");
321 if (urlStr ==
nullptr)
return false;
327 auto writer = [](Print& out,
void* ref) ->
size_t {
330 written += out.print(
"<AVTransportURI val=\"");
331 written += out.print(self->current_uri.c_str());
332 written += out.println(
"\"/>");
334 written += out.print(
"<AVTransportURIMetaData val=\"");
335 written += out.print(self->current_uri_metadata.c_str());
336 written += out.println(
"\"/>");
338 written += out.print(
"<NumberOfTracks val=\"1\"/>\n");
354 auto writer = [](Print& out,
void* ref) ->
size_t {
357 written += out.print(
"<TransportState val=\"");
358 written += out.print(self->getTransportState());
359 written += out.print(
"\"/>");
360 written += out.print(
"<CurrentTransportActions val=\"");
361 written += out.print(self->getCurrentTransportActions());
362 written += out.print(
"\"/>");
385 auto writer = [](Print& out,
void* ref) ->
size_t {
388 written += out.print(
"<TransportState val=\"");
389 written += out.print(self->transport_state.c_str());
390 written += out.print(
"\"/>");
391 written += out.print(
"<CurrentPlayMode val=\"NORMAL\"/>");
392 written += out.print(
"<CurrentTrackURI val=\"\"/>");
393 written += out.print(
"<RelativeTimePosition val=\"00:00:00\"/>");
394 written += out.print(
"<CurrentTransportActions val=\"Play\"/>");
403 return static_cast<size_t>(posMs / 1000);
408 auto writer = [](Print& out,
void* ref) ->
size_t {
412 int h =
static_cast<int>(self->getRelativeTimePositionSec() / 3600);
414 static_cast<int>((self->getRelativeTimePositionSec() % 3600) / 60);
415 int s =
static_cast<int>(self->getRelativeTimePositionSec() % 60);
416 written += out.print(
"<RelativeTimePosition val=\"");
418 if (h < 10) written += out.print(
'0');
419 written += out.print(h);
420 written += out.print(
':');
421 if (m < 10) written += out.print(
'0');
422 written += out.print(m);
423 written += out.print(
':');
424 if (s < 10) written += out.print(
'0');
425 written += out.print(s);
426 written += out.print(
"\"/>");
435 return "SetAVTransportURI";
447 for (
size_t i = 0; i <
rules.size(); ++i) {
449 rules[i].handler = handler;
453 rules.push_back({suffix, handler});
472 auto writer = [](Print& out,
void* ref) ->
size_t {
475 result += out.print(
"<SourceProtocolInfo val=\"");
476 result += out.print(
StrView(self->getSourceProtocols()).c_str());
477 result += out.println(
"\"/>");
478 result += out.print(
"<SinkProtocolInfo val=\"");
479 result += out.print(
StrView(self->getSinkProtocols()).c_str());
480 result += out.println(
"\"/>");
527 const char*
st =
"urn:schemas-upnp-org:device:MediaRenderer:1";
528 const char*
usn =
"uuid:09349455-2941-4cf7-9847-1dd5ab210e97";
556 std::function<
size_t(Print&,
void*)> changeWriter) {
564 auto writer = [](Print& out,
void* ref) ->
size_t {
568 written += outf.
printf(
"<TransportState val=\"%s\"/>",
569 self->transport_state.c_str());
570 if (!self->current_uri.isEmpty()) {
571 written += outf.printf(
"<CurrentTrackURI val=\"%s\"/>",
572 self->current_uri.c_str());
574 written += outf.printf(
575 "<RelativeTimePosition val=\"%02d:%02d:%02d\"/>",
576 static_cast<int>(self->getRelativeTimePositionSec() / 3600),
577 static_cast<int>((self->getRelativeTimePositionSec() % 3600) / 60),
578 static_cast<int>(self->getRelativeTimePositionSec() % 60));
579 written += outf.printf(
"<CurrentTransportActions val=\"%s\"/>",
580 self->getCurrentTransportActions());
588 auto writer = [](Print& out,
void* ref) ->
size_t {
592 written += outf.
printf(
"<Volume val=\"%d\" channel=\"Master\"/>", self->current_volume);
593 written += out.println();
594 written += outf.printf(
"<Mute val=\"%d\" channel=\"Master\"/>", self->isMuted() ? 1 : 0);
595 written += out.println();
603 auto writer = [](Print& out,
void* ref) ->
size_t {
606 return printer.
printf(
"<CurrentConnectionIDs>%s</CurrentConnectionIDs>");
626 char mimeBuf[256] = {0};
628 didl,
"<res",
"protocolInfo=", 3, mimeBuf,
sizeof(mimeBuf))) {
640 [](Print& out,
void* ref) ->
size_t {
649 "transportDescrCB: renderer instance not found for %s",
661 [](Print& out,
void* ref) ->
size_t {
670 "connmgrDescrCB: renderer instance not found for %s",
682 [](Print& out,
void* ref) ->
size_t {
691 "controlDescrCB: renderer instance not found for %s",
720 if (p_device ==
nullptr)
return nullptr;
726 const char* requestPath,
728 bool is_subscribe =
false;
732 assert(self !=
nullptr);
733 StrView request_path_str(requestPath);
734 if (request_path_str.
contains(
"/AVT/"))
736 else if (request_path_str.
contains(
"/RC/"))
738 else if (request_path_str.
contains(
"/CM/"))
747 avt.
setup(
"urn:schemas-upnp-org:service:AVTransport:1",
748 "urn:upnp-org:serviceId:AVTransport",
"/AVT/service.xml",
759 cm.
setup(
"urn:schemas-upnp-org:service:ConnectionManager:1",
760 "urn:upnp-org:serviceId:ConnectionManager",
"/CM/service.xml",
771 rc.
setup(
"urn:schemas-upnp-org:service:RenderingControl:1",
772 "urn:upnp-org:serviceId:RenderingControl",
"/RC/service.xml",
802 if (action_str.isEmpty()) {
808 for (
const auto& rule :
rules) {
809 if (action_str.endsWith(rule.suffix)) {
810 return rule.handler(
this, action, server, client);
824 [](Print& out,
void* ref) ->
size_t {
837 [](Print& out,
void* ref) ->
size_t {
839 out,
"ListPresetsResponse",
"AVTransport",
840 [](Print& o,
void* ) ->
size_t {
842 written += o.print(
"<CurrentPresetNameList>FactoryDefault</CurrentPresetNameList>");
855 [](Print& out,
void* ref) ->
size_t {
869 [](Print& out,
void* ref) ->
size_t {
882 [](Print& out,
void* ref) ->
size_t {
886 out,
"GetCurrentTransportActionsResponse",
"AVTransport",
887 [](Print& o,
void* innerRef) ->
size_t {
890 written += o.print(
"<Actions>");
891 written += o.print(self->getCurrentTransportActions());
892 written += o.print(
"</Actions>");
906 [](Print& out,
void* ref) ->
size_t {
909 out, self->getSourceProtocols(), self->getSinkProtocols());
920 [](Print& out,
void* ref) ->
size_t {
923 out, self->getConnectionID());
934 [](Print& out,
void* ref) ->
size_t {
937 out, self->getSinkProtocols(), self->getConnectionID(),
"Input");
948 [](Print& out,
void* ref) ->
size_t {
950 out,
"GetDeviceCapabilitiesResponse",
"AVTransport",
951 [](Print& o,
void* innerRef) ->
size_t {
954 written += o.print(
"<PlayMedia>");
956 StrView(self->getPossiblePlaybackStorageMedia()).
c_str());
957 written += o.print(
"</PlayMedia>");
958 written += o.print(
"<RecMedia>");
960 StrView(self->getPossibleRecordStorageMedia()).
c_str());
961 written += o.print(
"</RecMedia>");
962 written += o.print(
"<RecQualityModes>");
964 StrView(self->getPossibleRecordQualityModes()).
c_str());
965 written += o.print(
"</RecQualityModes>");
979 [](Print& out,
void* ref) ->
size_t {
981 out,
"GetTransportInfoResponse",
"AVTransport",
982 [](Print& o,
void* innerRef) ->
size_t {
986 written += o.print(
"<CurrentTransportState>");
987 written += o.print(self->getTransportState());
988 written += o.print(
"</CurrentTransportState>");
992 "<CurrentTransportStatus>OK</CurrentTransportStatus>");
995 written += o.print(
"<CurrentSpeed>1</CurrentSpeed>");
1000 200,
nullptr,
this);
1009 [](Print& out,
void* ref) ->
size_t {
1011 out,
"GetPositionInfoResponse",
"AVTransport",
1012 [](Print& o,
void* innerRef) ->
size_t {
1015 const bool hasUri = self->getCurrentUri() && *self->getCurrentUri();
1018 written += o.print(
"<Track>");
1019 written += o.print(hasUri ? 1 : 0);
1020 written += o.print(
"</Track>");
1023 written += o.print(
"<TrackDuration>00:00:00</TrackDuration>");
1026 written += o.print(
"<TrackMetaData>");
1027 written += o.print(
StrView(self->getCurrentUriMetadata()).
c_str());
1028 written += o.print(
"</TrackMetaData>");
1031 written += o.print(
"<TrackURI>");
1032 written += o.print(
StrView(self->getCurrentUri()).
c_str());
1033 written += o.print(
"</TrackURI>");
1036 unsigned long secs = self->getRelativeTimePositionSec();
1037 int h =
static_cast<int>(secs / 3600);
1038 int m =
static_cast<int>((secs % 3600) / 60);
1039 int s =
static_cast<int>(secs % 60);
1040 written += o.print(
"<RelTime>");
1041 if (h < 10) written += o.print(
'0');
1042 written += o.print(h);
1043 written += o.print(
':');
1044 if (m < 10) written += o.print(
'0');
1045 written += o.print(m);
1046 written += o.print(
':');
1047 if (s < 10) written += o.print(
'0');
1048 written += o.print(s);
1049 written += o.print(
"</RelTime>");
1052 written += o.print(
"<AbsTime>00:00:00</AbsTime>");
1055 written += o.print(
"<RelCount>2147483647</RelCount>");
1056 written += o.print(
"<AbsCount>2147483647</AbsCount>");
1062 200,
nullptr,
this);
1071 [](Print& out,
void* ref) ->
size_t {
1073 out,
"GetTransportSettingsResponse",
"AVTransport",
1074 [](Print& o,
void* innerRef) ->
size_t {
1078 written += o.print(
"<PlayMode>");
1079 written += o.print(
StrView(self->getPlayMode()).
c_str());
1080 written += o.print(
"</PlayMode>");
1084 written += o.print(
"<RecQualityMode>");
1085 written += o.print(
"NOT_IMPLEMENTED");
1086 written += o.print(
"</RecQualityMode>");
1091 200,
nullptr,
this);
1099 [](Print& out,
void* ref) ->
size_t {
1101 out,
"GetMediaInfoResponse",
"AVTransport",
1102 [](Print& o,
void* innerRef) ->
size_t {
1107 written += o.print(
"<NrTracks>");
1109 self->getCurrentUri() && *self->getCurrentUri() ? 1 : 0);
1110 written += o.print(
"</NrTracks>");
1113 written += o.print(
"<MediaDuration></MediaDuration>");
1116 written += o.print(
"<CurrentURI>");
1117 written += o.print(
StrView(self->getCurrentUri()).
c_str());
1118 written += o.print(
"</CurrentURI>");
1119 written += o.print(
"<CurrentURIMetaData>");
1121 o.print(
StrView(self->getCurrentUriMetadata()).
c_str());
1122 written += o.print(
"</CurrentURIMetaData>");
1125 written += o.print(
"<NextURI>NOT_IMPLEMENTED</NextURI>");
1127 "<NextURIMetaData>NOT_IMPLEMENTED</NextURIMetaData>");
1131 written += o.print(
"<PlayMedium>");
1133 StrView(self->getPossiblePlaybackStorageMedia()).
c_str());
1134 written += o.print(
"</PlayMedium>");
1136 written += o.print(
"<RecordMedium>");
1138 StrView(self->getPossibleRecordStorageMedia()).
c_str());
1139 written += o.print(
"</RecordMedium>");
1142 o.print(
"<WriteStatus>NOT_IMPLEMENTED</WriteStatus>");
1148 200,
nullptr,
this);
1161 [](Print& out,
void* ref) ->
size_t {
1166 200,
nullptr,
this);
1170 "SetAVTransportURI called with invalid SOAP payload");
1182 [](Print& out,
void* ref) ->
size_t {
1185 "RenderingControl");
1187 200,
nullptr,
this);
1197 [](Print& out,
void* ref) ->
size_t {
1200 "RenderingControl");
1202 200,
nullptr,
this);
1209 [](Print& out,
void* ref) ->
size_t {
1211 out,
"GetMuteResponse",
"RenderingControl",
1212 [](Print& o,
void* innerRef) ->
size_t {
1215 written += o.print(
"<CurrentMute>");
1216 written += o.print(self->isMuted() ? 1 : 0);
1217 written += o.print(
"</CurrentMute>");
1222 200,
nullptr,
this);
1229 [](Print& out,
void* ref) ->
size_t {
1231 out,
"GetVolumeResponse",
"RenderingControl",
1232 [](Print& o,
void* innerRef) ->
size_t {
1237 written += o.print(
"<CurrentVolume>");
1238 written += o.print((
int)self->getVolume());
1239 written += o.print(
"</CurrentVolume>");
1244 200,
nullptr,
this);
1301 rules.push_back({
"GetCurrentConnectionIDs",
1305 action, server, client);
1307 rules.push_back({
"GetCurrentConnectionInfo",
1311 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:82
void addService(DLNAServiceInfo s)
Adds a service definition.
Definition: DLNADeviceInfo.h:180
void setModelName(const char *name)
Definition: DLNADeviceInfo.h:166
void setSerialNumber(const char *sn)
Definition: DLNADeviceInfo.h:170
void setManufacturer(const char *man)
Definition: DLNADeviceInfo.h:160
void setUDN(const char *id)
Define the udn uuid.
Definition: DLNADeviceInfo.h:87
void setFriendlyName(const char *name)
Definition: DLNADeviceInfo.h:158
bool is_active
Definition: DLNADeviceInfo.h:283
void setModelNumber(const char *number)
Definition: DLNADeviceInfo.h:168
void setBaseURL(const char *url)
Defines the base url.
Definition: DLNADeviceInfo.h:93
Setup of a Basic DLNA Device service. The device registers itself to the network and answers to the D...
Definition: DLNADevice.h:46
void end() override
Stops the processing and releases the resources.
Definition: DLNADevice.h:118
void setReference(void *ref) override
Sets a reference pointer that can be used to associate application.
Definition: DLNADevice.h:452
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:334
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:274
bool begin(DLNADeviceInfo &device, IUDPService &udp, IHttpServer &server) override
start the
Definition: DLNADevice.h:53
static size_t replyGetCurrentConnectionInfo(Print &out, const char *protocolInfo, const char *connectionID, const char *direction)
Definition: DLNADevice.h:397
bool loop(int loopAction=RUN_ALL) override
Definition: DLNADevice.h:141
static size_t replyGetProtocolInfo(Print &out, const char *source="", const char *sink="")
Definition: DLNADevice.h:364
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:427
static size_t replyGetCurrentConnectionIDs(Print &out, const char *ids)
Definition: DLNADevice.h:381
void setSubscriptionsActive(bool flag) override
Enable or disable subscription notifications: call before begin.
Definition: DLNADevice.h:260
bool isSubscriptionsActive() const override
Check if subscription notifications are active.
Definition: DLNADevice.h:269
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:231
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
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
#define DLNA_PROTOCOL_AUDIO
All possible audio protocols.
Definition: dlna_config.h:154
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:32
@ RUN_ALL
Definition: IDevice.h:18
void(* http_callback)(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Definition: DLNAServiceInfo.h:11