Arduino DLNA Server
Loading...
Searching...
No Matches
DLNAMediaRenderer.h
Go to the documentation of this file.
1#pragma once
2#include <cctype>
3#include <cstring>
4#include <functional>
5
8#include "basic/Printf.h"
9#include "basic/Str.h"
15
16namespace tiny_dlna {
34
70template <typename ClientType>
72 public:
73 using HttpClient = ClientType;
75 // event handler: (event, reference to MediaRenderer)
76 typedef void (*MediaEventHandler)(MediaEvent event,
77 DLNAMediaRenderer& renderer);
78
87 // Constructor
88 DlnaLogger.log(DlnaLogLevel::Info, "MediaRenderer::MediaRenderer");
89 setUDN(usn);
90 setSerialNumber("1.0");
92 setFriendlyName("ArduinoMediaRenderer");
93 setManufacturer("TinyDLNA");
94 setModelName("TinyDLNA");
95 setModelNumber("1.0");
96 setBaseURL("http://localhost:44757");
97 setupRules();
99 }
100
108 setHttpServer(server);
109 setUdpService(udp);
110 }
111
113 bool begin() { return dlna_device.begin(*this, *p_udp_member, *p_server); }
114
116 void end() {
117 if (is_active) stop();
119 }
120
122 bool loop(int loopAction = RUN_ALL) { return dlna_device.loop(loopAction); }
123
126 p_server = &server;
127 // prepare handler context and register each service via helpers
128 server.setReference(this);
129 setupServicesImpl(&server);
130 }
131
134
137 possiblePlaybackStorageMedia = v ? v : "";
138 }
139
143 }
144
146 void setPossibleRecordStorageMedia(const char* v) {
147 possibleRecordStorageMedia = v ? v : "";
148 }
149
151 const char* getPossibleRecordStorageMedia() const {
153 }
154
156 void setPossibleRecordQualityModes(const char* v) {
157 possibleRecordQualityModes = v ? v : "";
158 }
159
161 const char* getPossibleRecordQualityModes() const {
163 }
164
166 void setPlayMode(const char* v) { currentPlayMode = v ? v : "NORMAL"; }
167
169 const char* getPlayMode() const { return currentPlayMode; }
170
172 bool isActive() { return is_active; }
173
175 void setActive(bool active) {
176 DlnaLogger.log(DlnaLogLevel::Info, "Set active: %s",
177 active ? "true" : "false");
178 is_active = active;
179 if (active) {
180 start_time = millis();
181 } else {
182 // accumulate time
183 time_sum += millis() - start_time;
184 start_time = 0;
185 }
186 // reflect state in transport_state so deferred writer can read it
187 transport_state = active ? "PLAYING" : "PAUSED_PLAYBACK";
188 struct StateWriter {
189 static size_t write(Print& out, void* ref) {
190 auto self = (DLNAMediaRenderer*)ref;
191 size_t written = 0;
192
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("\"/>");
199
200 return written;
201 }
202 };
203 addChange("AVT", StateWriter::write);
204 }
205
207 const char* getMime() {
208 // Prefer explicit MIME from DIDL if available
209 if (!current_mime.isEmpty()) return current_mime.c_str();
210 return nullptr;
211 }
212
215
217 uint8_t getVolume() { return current_volume; }
218
220 void setVolume(uint8_t vol) {
221 current_volume = vol;
222 (void)current_volume; // value used by writer via ref
223 auto writer = [](Print& out, void* ref) -> size_t {
224 auto self = (DLNAMediaRenderer*)ref;
225 size_t written = 0;
226 written += out.print("<Volume val=\"");
227 written += out.print(self->current_volume);
228 written += out.print("\" channel=\"Master\"/>");
229 return written;
230 };
231 addChange("RCS", writer);
232 }
233
235 bool isMuted() { return getVolume() == 0; }
236
238 void setMuted(bool mute) {
239 if (mute) {
240 // store current volume and set to 0
242 current_volume = 0;
243 } else {
244 // restore previous volume
246 }
247 auto writer = [](Print& out, void* ref) -> size_t {
248 auto self = (DLNAMediaRenderer*)ref;
249 size_t written = 0;
250 written += out.print("<Mute val=\"");
251 written += out.print(self->isMuted() ? 1 : 0);
252 written += out.print("\" channel=\"Master\"/>");
253 return written;
254 };
255 addChange("RCS", writer);
256 }
257
259 const char* getCurrentUri() { return current_uri.c_str(); }
260
263
265 const char* getTitle() { return current_title.c_str(); }
266
269 const char* getArtist() { return current_artist.c_str(); }
270
272 const char* getAlbum() { return current_album.c_str(); }
273
275 const char* getTransportState() { return transport_state.c_str(); }
276
279
281 void setSubscriptionsActive(bool flag) {
283 }
284
287
289 bool play() {
290 setActive(true);
291 return true;
292 }
293
295 bool play(const char* urlStr) {
296 if (urlStr == nullptr) return false;
297 DlnaLogger.log(DlnaLogLevel::Info, "play URL: %s", urlStr);
298 // store URI
299 current_uri.set(urlStr);
300 // notify handler about the new URI; do not dispatch PLAY here — action
301 // handlers will dispatch media events explicitly.
302 is_active = true;
303
304 // ensure transport_state reflects playing for subscribers
305 transport_state = "PLAYING";
306 (void)current_uri; // writer will fetch it from ref
307 auto writer = [](Print& out, void* ref) -> size_t {
308 auto self = (DLNAMediaRenderer*)ref;
309 size_t written = 0;
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=\"");
315 EscapingPrint esc{out};
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("\"/>");
324 return written;
325 };
326 addChange("AVT", writer);
327
328 return true;
329 }
330
332 bool setPlaybackURL(const char* urlStr) {
333 if (urlStr == nullptr) return false;
334 DlnaLogger.log(DlnaLogLevel::Info, "setPlaybackURL URL: %s", urlStr);
335 // store URI
336 current_uri = Str(urlStr);
337 // Do not notify application here; event notifications are emitted
338 // from the SOAP action handlers (processAction*).
339 auto writer = [](Print& out, void* ref) -> size_t {
340 auto self = (DLNAMediaRenderer*)ref;
341 size_t written = 0;
342 written += out.print("<AVTransportURI val=\"");
343 written += out.print(self->current_uri.c_str());
344 written += out.println("\"/>");
345
346 written += out.print("<AVTransportURIMetaData val=\"");
347 EscapingPrint esc{out};
348 written += esc.print(self->current_uri_metadata.c_str());
349 written += out.println("\"/>");
350
351 written += out.print("<NumberOfTracks val=\"1\"/>\n");
352 return written;
353 };
354 addChange("AVT", writer);
355
356 return true;
357 }
358
360 bool stop() {
361 DlnaLogger.log(DlnaLogLevel::Info, "Stop playback");
362 is_active = false;
363 start_time = 0;
364 time_sum = 0;
365 // reflect stopped state
366 transport_state = "STOPPED";
367 auto writer = [](Print& out, void* ref) -> size_t {
368 auto self = (DLNAMediaRenderer*)ref;
369 size_t written = 0;
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("\"/>");
376 return written;
377 };
378 addChange("AVT", writer);
379 return true;
380 }
381
391 DlnaLogger.log(DlnaLogLevel::Info, "Playback completed");
392 transport_state = "STOPPED";
393 is_active = false;
394 start_time = 0;
395 time_sum = 0;
396 // Do not notify application here; event notifications are emitted
397 // from the SOAP action handlers (processAction*).
398 auto writer = [](Print& out, void* ref) -> size_t {
399 auto self = (DLNAMediaRenderer*)ref;
400 size_t written = 0;
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\"/>");
408 return written;
409 };
410 addChange("AVT", writer);
411 }
412
415 unsigned long posMs = (millis() - start_time) + time_sum;
416 return static_cast<size_t>(posMs / 1000);
417 }
418
421 auto writer = [](Print& out, void* ref) -> size_t {
422 auto self = (DLNAMediaRenderer*)ref;
423 size_t written = 0;
424 // format hh:mm:ss by printing parts
425 int h = static_cast<int>(self->getRelativeTimePositionSec() / 3600);
426 int m =
427 static_cast<int>((self->getRelativeTimePositionSec() % 3600) / 60);
428 int s = static_cast<int>(self->getRelativeTimePositionSec() % 60);
429 written += out.print("<RelativeTimePosition val=\"");
430 // print two-digit hour
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("\"/>");
440 return written;
441 };
442 addChange("AVT", writer);
443 }
444
447 if (current_uri.isEmpty()) {
448 return "SetAVTransportURI";
449 }
450 if (is_active) {
451 return "Pause,Stop";
452 } else {
453 return "Play";
454 }
455 }
457 void setCustomActionRule(const char* suffix,
458 bool (*handler)(DLNAMediaRenderer*, ActionRequest&,
459 IHttpServer&)) {
460 for (size_t i = 0; i < rules.size(); ++i) {
461 if (StrView(rules[i].suffix).equals(suffix)) {
462 rules[i].handler = handler;
463 return;
464 }
465 }
466 rules.push_back({suffix, handler});
467 }
468
470 void setConnectionID(const char* id) { connectionID = id; }
471
473 const char* getConnectionID() { return connectionID; }
474
480 void setProtocols(const char* source, const char* sink = "") {
481 sourceProto = source;
482 sinkProto = sink;
483 // publish protocol info change to ConnectionManager subscribers
484 // Build writer that will use live state from the device at publish time
485 auto writer = [](Print& out, void* ref) -> size_t {
486 auto self = (DLNAMediaRenderer*)ref;
487 size_t result = 0;
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("\"/>");
494 return result;
495 };
496 addChange("CMS", writer);
497 }
498
500 const char* getSourceProtocols() { return sourceProto; }
501
503 const char* getSinkProtocols() { return sinkProto; }
504
507
510
513
516
519
522
523 protected:
525 struct ActionRule {
526 const char* suffix;
528 };
529
537 uint8_t current_volume = 50;
538 uint8_t muted_volume = 0;
539 unsigned long start_time = 0;
540 unsigned long time_sum = 0;
541 // Current state (e.g. "STOPPED", "PLAYING","PAUSED_PLAYBACK")
543 const char* st = "urn:schemas-upnp-org:device:MediaRenderer:1";
544 const char* usn = "uuid:09349455-2941-4cf7-9847-1dd5ab210e97";
548 const char* connectionID = "0";
549 const char* sourceProto = "";
551 const char* possiblePlaybackStorageMedia = "NETWORK";
552 const char* possibleRecordStorageMedia = "NONE";
553 const char* possibleRecordQualityModes = "NOT_IMPLEMENTED";
554 const char* currentPlayMode = "NORMAL";
556
557 // Descriptor instances so callers can customise the SCPD output per device
564
565 // Note: descriptor printing is performed inline in the descriptor
566 // callbacks below using non-capturing lambdas passed to
567 // IHttpServer::reply. That avoids heap buffering while keeping the
568 // callsites concise. The old static helper functions were removed.
569
571 void addChange(const char* serviceAbbrev,
572 std::function<size_t(Print&, void*)> changeWriter) {
573 dlna_device.addChange(serviceAbbrev, changeWriter, this);
574 }
575
578 void publishAVT() {
579 // Transport state will be built by the writer from the instance (ref)
580 auto writer = [](Print& out, void* ref) -> size_t {
581 auto self = (DLNAMediaRenderer*)ref;
582 Printf outf{out};
583 size_t written = 0;
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());
589 }
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());
597 return written;
598 };
599 addChange("AVT", writer);
600 }
601
603 void publishRCS() {
604 auto writer = [](Print& out, void* ref) -> size_t {
605 auto self = (DLNAMediaRenderer*)ref;
606 Printf outf{out};
607 size_t written = 0;
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();
612 return written;
613 };
614 addChange("RCS", writer);
615 }
616
618 void publishCMS() {
619 auto writer = [](Print& out, void* ref) -> size_t {
621 Printf printer{out};
622 return printer.printf("<CurrentConnectionIDs>%s</CurrentConnectionIDs>");
623 };
624 addChange("CMS", writer);
625 }
626
628 void setMime(const char* mime) {
629 if (mime) {
630 current_mime = mime;
631 DlnaLogger.log(DlnaLogLevel::Info, "Set mime: %s", current_mime.c_str());
632 }
633 }
634
637 void setMimeFromDIDL(const char* didl) {
638 if (!didl) return;
639 // Convenience helper: extract protocolInfo attribute from a tag and
640 // return the contentFormat (3rd token) portion, e.g. from
641 // protocolInfo="http-get:*:audio/mpeg:*" -> "audio/mpeg".
642 char mimeBuf[256] = {0};
644 didl, "<res", "protocolInfo=", 3, mimeBuf, sizeof(mimeBuf))) {
645 setMime(mimeBuf);
646 }
647 }
648
650 void setMetadata(const char* didl) {
651 current_uri_metadata = didl ? didl : "";
656 if (current_uri_metadata.isEmpty()) return;
657 const char* xml = current_uri_metadata.c_str();
658 setMimeFromDIDL(xml);
660 if (!XMLAttributeParser::extractElementText(xml, "upnp:artist",
664 }
666 DlnaLogger.log(DlnaLogLevel::Info, "Title: %s", current_title.c_str());
667 }
668
669 // Static descriptor callbacks: print the SCPD XML for each service
670 static void transportDescrCB(IClientHandler& client, IHttpServer* server, const char* requestPath,
672 DLNAMediaRenderer* self = getRenderer(server);
673 if (self) {
674 client.reply(
675 "text/xml",
676 [](Print& out, void* ref) -> size_t {
677 size_t result = 0;
678 auto self = static_cast<DLNAMediaRenderer*>(ref);
679 if (self) result += self->getTransportDescr().printDescr(out);
680 return result;
681 },
682 200, nullptr, self);
683 } else {
684 DlnaLogger.log(DlnaLogLevel::Error,
685 "transportDescrCB: renderer instance not found for %s",
686 requestPath);
687 client.replyNotFound();
688 }
689 }
690
691 static void connmgrDescrCB(IClientHandler& client, IHttpServer* server, const char* requestPath,
693 DLNAMediaRenderer* self = getRenderer(server);
694 if (self) {
695 client.reply(
696 "text/xml",
697 [](Print& out, void* ref) -> size_t {
698 size_t result = 0;
699 auto self = static_cast<DLNAMediaRenderer*>(ref);
700 if (self) result += self->getConnectionMgrDescr().printDescr(out);
701 return result;
702 },
703 200, nullptr, self);
704 } else {
705 DlnaLogger.log(DlnaLogLevel::Error,
706 "connmgrDescrCB: renderer instance not found for %s",
707 requestPath);
708 client.replyNotFound();
709 }
710 }
711
712 static void controlDescrCB(IClientHandler& client, IHttpServer* server, const char* requestPath,
714 DLNAMediaRenderer* self = getRenderer(server);
715 if (self) {
716 client.reply(
717 "text/xml",
718 [](Print& out, void* ref) -> size_t {
719 size_t result = 0;
720 auto self = static_cast<DLNAMediaRenderer*>(ref);
721 if (self) result += self->getControlDescr().printDescr(out);
722 return result;
723 },
724 200, nullptr, self);
725 } else {
726 DlnaLogger.log(DlnaLogLevel::Error,
727 "controlDescrCB: renderer instance not found for %s",
728 requestPath);
729 client.replyNotFound();
730 }
731 }
732
733 // Generic control callback: parse SOAP action and dispatch to instance
734 static void controlCB(IClientHandler& client, IHttpServer* server, const char* requestPath,
736 ActionRequest action;
737 DeviceType::parseActionRequest(server, client, requestPath, hl, action);
738
739 DLNAMediaRenderer* self = getRenderer(server);
740
741 // Log incoming SOAPAction header and parsed action for debugging
742 const char* soapHdr = client.requestHeader().get("SOAPACTION");
743 DlnaLogger.log(DlnaLogLevel::Debug, "controlCB: SOAPAction=%s, action=%s",
744 StrView(soapHdr).c_str(),
745 StrView(action.getAction()).c_str());
746
747 if (self) {
748 if (self->processAction(action, *server, client)) return;
749 }
750 client.replyNotFound();
751 }
752
755 auto* p_device = static_cast<IDevice*>(server->getReference());
756 if (p_device == nullptr) return nullptr;
757 return static_cast<DLNAMediaRenderer*>(p_device->getReference());
758 }
759
762 const char* requestPath,
764 bool is_subscribe = false;
765 DeviceType::handleSubscription(server, &client, requestPath, hl, is_subscribe);
766 if (is_subscribe) {
767 DLNAMediaRenderer* self = getRenderer(server);
768 assert(self != nullptr);
769 StrView request_path_str(requestPath);
770 if (request_path_str.contains("/AVT/"))
771 self->publishAVT();
772 else if (request_path_str.contains("/RC/"))
773 self->publishRCS();
774 else if (request_path_str.contains("/CM/"))
775 self->publishCMS();
776 }
777 }
778
779 // Setup and register individual services
781 DLNAServiceInfo avt;
782 // Use static descriptor callback and control handler
783 avt.setup("urn:schemas-upnp-org:service:AVTransport:1",
784 "urn:upnp-org:serviceId:AVTransport", "/AVT/service.xml",
789
790 addService(avt);
791 }
792
795 cm.setup("urn:schemas-upnp-org:service:ConnectionManager:1",
796 "urn:upnp-org:serviceId:ConnectionManager", "/CM/service.xml",
797 &DLNAMediaRenderer::connmgrDescrCB, "/CM/control",
801
802 addService(cm);
803 }
804
807 rc.setup("urn:schemas-upnp-org:service:RenderingControl:1",
808 "urn:upnp-org:serviceId:RenderingControl", "/RC/service.xml",
809 &DLNAMediaRenderer::controlDescrCB, "/RC/control",
813
814 addService(rc);
815 }
816
819 DlnaLogger.log(DlnaLogLevel::Info, "MediaRenderer::setupServices");
820 // register the individual services and register their endpoints on the
821 // provided HTTP server
822 setupTransportService(server);
825 }
826
834 bool processAction(ActionRequest& action, IHttpServer& server, IClientHandler& client) {
835 DlnaLogger.log(DlnaLogLevel::Info, "DLNAMediaRenderer::processAction: %s",
836 action.getAction());
837 auto& action_str = action.getActionStr();
838 if (action_str.isEmpty()) {
839 DlnaLogger.log(DlnaLogLevel::Error, "Empty action received");
840 client.replyError(400, "Invalid Action");
841 return false;
842 }
843
844 for (const auto& rule : rules) {
845 if (action_str.endsWith(rule.suffix)) {
846 return rule.handler(this, action, server, client);
847 }
848 }
849 DlnaLogger.log(DlnaLogLevel::Error, "Unsupported action: %s",
850 action.getAction());
851 client.replyError(400, "Invalid Action");
852 return false;
853 }
854
855 // Per-action handlers (match MediaServer pattern)
858 client.reply(
859 "text/xml",
860 [](Print& out, void* ref) -> size_t {
861 (void)ref;
862 return DeviceType::printReplyXML(out, "PlayResponse", "AVTransport");
863 },
864 200, nullptr, this);
865 // setActive(true);
866 play();
867 return true;
868 }
869
871 client.reply(
872 "text/xml",
873 [](Print& out, void* ref) -> size_t {
875 out, "ListPresetsResponse", "AVTransport",
876 [](Print& o, void* /*innerRef*/) -> size_t {
877 size_t written = 0;
878 written += o.print("<CurrentPresetNameList>FactoryDefault</CurrentPresetNameList>");
879 return written;
880 },
881 nullptr);
882 },
883 200, nullptr, this);
884 return true;
885 }
886
889 client.reply(
890 "text/xml",
891 [](Print& out, void* ref) -> size_t {
892 (void)ref;
893 return DeviceType::printReplyXML(out, "PauseResponse", "AVTransport");
894 },
895 200, nullptr, this);
896 setActive(false);
897 return true;
898 }
899
901 // use stop() to ensure state reset and application callback are invoked
903 client.reply(
904 "text/xml",
905 [](Print& out, void* ref) -> size_t {
906 (void)ref;
907 return DeviceType::printReplyXML(out, "StopResponse", "AVTransport");
908 },
909 200, nullptr, this);
910 stop();
911 return true;
912 }
913
915 IHttpServer& server, IClientHandler& client) {
916 client.reply(
917 "text/xml",
918 [](Print& out, void* ref) -> size_t {
919 // Stream reply directly: delegate to printReplyXML which will
920 // write headers and invoke the inner writer with the same ref.
922 out, "GetCurrentTransportActionsResponse", "AVTransport",
923 [](Print& o, void* innerRef) -> size_t {
924 size_t written = 0;
925 auto self = (DLNAMediaRenderer*)innerRef;
926 written += o.print("<Actions>");
927 written += o.print(self->getCurrentTransportActions());
928 written += o.print("</Actions>");
929 return written;
930 },
931 ref);
932 },
933 200, nullptr, this);
934 return true;
935 }
936
937 // ConnectionManager: GetProtocolInfo
939 IHttpServer& server, IClientHandler& client) {
940 client.reply(
941 "text/xml",
942 [](Print& out, void* ref) -> size_t {
943 auto self = (DLNAMediaRenderer*)ref;
945 out, self->getSourceProtocols(), self->getSinkProtocols());
946 },
947 200, nullptr, this);
948 return true;
949 }
950
951 // ConnectionManager: GetCurrentConnectionIDs
953 IHttpServer& server, IClientHandler& client) {
954 client.reply(
955 "text/xml",
956 [](Print& out, void* ref) -> size_t {
957 auto self = (DLNAMediaRenderer*)ref;
959 out, self->getConnectionID());
960 },
961 200, nullptr, this);
962 return true;
963 }
964
965 // ConnectionManager: GetCurrentConnectionInfo
967 IHttpServer& server, IClientHandler& client) {
968 client.reply(
969 "text/xml",
970 [](Print& out, void* ref) -> size_t {
971 auto self = (DLNAMediaRenderer*)ref;
973 out, self->getSinkProtocols(), self->getConnectionID(), "Input");
974 },
975 200, nullptr, this);
976 return true;
977 }
978
979 // AVTransport: GetDeviceCapabilities
981 IHttpServer& server, IClientHandler& client) {
982 client.reply(
983 "text/xml",
984 [](Print& out, void* ref) -> size_t {
986 out, "GetDeviceCapabilitiesResponse", "AVTransport",
987 [](Print& o, void* innerRef) -> size_t {
988 auto self = (DLNAMediaRenderer*)innerRef;
989 size_t written = 0;
990 written += o.print("<PlayMedia>");
991 written += o.print(
992 StrView(self->getPossiblePlaybackStorageMedia()).c_str());
993 written += o.print("</PlayMedia>");
994 written += o.print("<RecMedia>");
995 written += o.print(
996 StrView(self->getPossibleRecordStorageMedia()).c_str());
997 written += o.print("</RecMedia>");
998 written += o.print("<RecQualityModes>");
999 written += o.print(
1000 StrView(self->getPossibleRecordQualityModes()).c_str());
1001 written += o.print("</RecQualityModes>");
1002 return written;
1003 },
1004 ref);
1005 },
1006 200, nullptr, this);
1007 return true;
1008 }
1009
1010 // AVTransport: GetTransportInfo
1012 IHttpServer& server, IClientHandler& client) {
1013 client.reply(
1014 "text/xml",
1015 [](Print& out, void* ref) -> size_t {
1017 out, "GetTransportInfoResponse", "AVTransport",
1018 [](Print& o, void* innerRef) -> size_t {
1019 auto self = (DLNAMediaRenderer*)innerRef;
1020 size_t written = 0;
1021 // CurrentTransportState
1022 written += o.print("<CurrentTransportState>");
1023 written += o.print(self->getTransportState());
1024 written += o.print("</CurrentTransportState>");
1025
1026 // CurrentTransportStatus - return OK by default
1027 written += o.print(
1028 "<CurrentTransportStatus>OK</CurrentTransportStatus>");
1029
1030 // CurrentSpeed - simple renderer: report "1"
1031 written += o.print("<CurrentSpeed>1</CurrentSpeed>");
1032 return written;
1033 },
1034 ref);
1035 },
1036 200, nullptr, this);
1037 return true;
1038 }
1039
1040 // AVTransport: GetPositionInfo
1042 IHttpServer& server, IClientHandler& client) {
1043 client.reply(
1044 "text/xml",
1045 [](Print& out, void* ref) -> size_t {
1047 out, "GetPositionInfoResponse", "AVTransport",
1048 [](Print& o, void* innerRef) -> size_t {
1049 auto self = (DLNAMediaRenderer*)innerRef;
1050 size_t written = 0;
1051 const bool hasUri = self->getCurrentUri() && *self->getCurrentUri();
1052
1053 // Track (1 if URI present, else 0)
1054 written += o.print("<Track>");
1055 written += o.print(hasUri ? 1 : 0);
1056 written += o.print("</Track>");
1057
1058 // TrackDuration (unknown -> 00:00:00)
1059 written += o.print("<TrackDuration>00:00:00</TrackDuration>");
1060
1061 // TrackMetaData (DIDL-Lite or empty)
1062 written += o.print("<TrackMetaData>");
1063 EscapingPrint esc{o};
1064 written += esc.print(self->getCurrentUriMetadata());
1065 written += o.print("</TrackMetaData>");
1066
1067 // TrackURI
1068 written += o.print("<TrackURI>");
1069 written += o.print(StrView(self->getCurrentUri()).c_str());
1070 written += o.print("</TrackURI>");
1071
1072 // RelTime (hh:mm:ss based on getRelativeTimePositionSec)
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>");
1087
1088 // AbsTime (unknown -> 00:00:00)
1089 written += o.print("<AbsTime>00:00:00</AbsTime>");
1090
1091 // RelCount, AbsCount (unknown -> 2147483647)
1092 written += o.print("<RelCount>2147483647</RelCount>");
1093 written += o.print("<AbsCount>2147483647</AbsCount>");
1094
1095 return written;
1096 },
1097 ref);
1098 },
1099 200, nullptr, this);
1100 return true;
1101 }
1102
1103 // AVTransport: GetTransportSettings
1105 IHttpServer& server, IClientHandler& client) {
1106 client.reply(
1107 "text/xml",
1108 [](Print& out, void* ref) -> size_t {
1110 out, "GetTransportSettingsResponse", "AVTransport",
1111 [](Print& o, void* innerRef) -> size_t {
1112 auto self = (DLNAMediaRenderer*)innerRef;
1113 size_t written = 0;
1114 // PlayMode: report configured play mode
1115 written += o.print("<PlayMode>");
1116 written += o.print(StrView(self->getPlayMode()).c_str());
1117 written += o.print("</PlayMode>");
1118
1119 // RecQualityMode: single value - report NOT_IMPLEMENTED by
1120 // default
1121 written += o.print("<RecQualityMode>");
1122 written += o.print("NOT_IMPLEMENTED");
1123 written += o.print("</RecQualityMode>");
1124 return written;
1125 },
1126 ref);
1127 },
1128 200, nullptr, this);
1129 return true;
1130 }
1131
1132 // AVTransport: GetMediaInfo
1134 client.reply(
1135 "text/xml",
1136 [](Print& out, void* ref) -> size_t {
1138 out, "GetMediaInfoResponse", "AVTransport",
1139 [](Print& o, void* innerRef) -> size_t {
1140 auto self = (DLNAMediaRenderer*)innerRef;
1141 size_t written = 0;
1142 // NrTracks: simple heuristic - 1 if a URI is present, 0
1143 // otherwise
1144 written += o.print("<NrTracks>");
1145 written += o.print(
1146 self->getCurrentUri() && *self->getCurrentUri() ? 1 : 0);
1147 written += o.print("</NrTracks>");
1148
1149 // MediaDuration: unknown here -> empty string
1150 written += o.print("<MediaDuration></MediaDuration>");
1151
1152 // CurrentURI and CurrentURIMetaData
1153 written += o.print("<CurrentURI>");
1154 written += o.print(StrView(self->getCurrentUri()).c_str());
1155 written += o.print("</CurrentURI>");
1156 written += o.print("<CurrentURIMetaData>");
1157 EscapingPrint esc{o};
1158 written += esc.print(self->getCurrentUriMetadata());
1159 written += o.print("</CurrentURIMetaData>");
1160
1161 // NextURI and NextURIMetaData: not supported -> empty
1162 written += o.print("<NextURI>NOT_IMPLEMENTED</NextURI>");
1163 written += o.print(
1164 "<NextURIMetaData>NOT_IMPLEMENTED</NextURIMetaData>");
1165
1166 // PlayMedium / RecordMedium / WriteStatus: use configured
1167 // defaults
1168 written += o.print("<PlayMedium>");
1169 written += o.print(
1170 StrView(self->getPossiblePlaybackStorageMedia()).c_str());
1171 written += o.print("</PlayMedium>");
1172
1173 written += o.print("<RecordMedium>");
1174 written += o.print(
1175 StrView(self->getPossibleRecordStorageMedia()).c_str());
1176 written += o.print("</RecordMedium>");
1177
1178 written +=
1179 o.print("<WriteStatus>NOT_IMPLEMENTED</WriteStatus>");
1180
1181 return written;
1182 },
1183 ref);
1184 },
1185 200, nullptr, this);
1186 return true;
1187 }
1188
1190 IHttpServer& server, IClientHandler& client) {
1191 const char* uri = action.getArgumentValue("CurrentURI");
1192 setMetadata(action.getArgumentValue("CurrentURIMetaData"));
1193 if (uri && *uri) {
1194 setPlaybackURL(uri);
1196 client.reply(
1197 "text/xml",
1198 [](Print& out, void* ref) -> size_t {
1199 (void)ref;
1200 return DeviceType::printReplyXML(out, "SetAVTransportURIResponse",
1201 "AVTransport");
1202 },
1203 200, nullptr, this);
1204 return true;
1205 } else {
1206 DlnaLogger.log(DlnaLogLevel::Warning,
1207 "SetAVTransportURI called with invalid SOAP payload");
1208 client.replyError(400, "Invalid Action");
1209 return false;
1210 }
1211 }
1212
1214 int desiredVolume = action.getArgumentIntValue("DesiredVolume");
1215 setVolume((uint8_t)desiredVolume);
1217 client.reply(
1218 "text/xml",
1219 [](Print& out, void* ref) -> size_t {
1220 (void)ref;
1221 return DeviceType::printReplyXML(out, "SetVolumeResponse",
1222 "RenderingControl");
1223 },
1224 200, nullptr, this);
1225 return true;
1226 }
1227
1229 bool desiredMute = action.getArgumentIntValue("DesiredMute") != 0;
1230 setMuted(desiredMute);
1232 client.reply(
1233 "text/xml",
1234 [](Print& out, void* ref) -> size_t {
1235 (void)ref;
1236 return DeviceType::printReplyXML(out, "SetMuteResponse",
1237 "RenderingControl");
1238 },
1239 200, nullptr, this);
1240 return true;
1241 }
1242
1244 client.reply(
1245 "text/xml",
1246 [](Print& out, void* ref) -> size_t {
1248 out, "GetMuteResponse", "RenderingControl",
1249 [](Print& o, void* innerRef) -> size_t {
1250 auto self = (DLNAMediaRenderer*)innerRef;
1251 size_t written = 0;
1252 written += o.print("<CurrentMute>");
1253 written += o.print(self->isMuted() ? 1 : 0);
1254 written += o.print("</CurrentMute>");
1255 return written;
1256 },
1257 ref);
1258 },
1259 200, nullptr, this);
1260 return true;
1261 }
1262
1264 client.reply(
1265 "text/xml",
1266 [](Print& out, void* ref) -> size_t {
1268 out, "GetVolumeResponse", "RenderingControl",
1269 [](Print& o, void* innerRef) -> size_t {
1270 auto self = (DLNAMediaRenderer*)innerRef;
1271 DlnaLogger.log(DlnaLogLevel::Debug, "GetVolume: %d",
1272 self->getVolume());
1273 size_t written = 0;
1274 written += o.print("<CurrentVolume>");
1275 written += o.print((int)self->getVolume());
1276 written += o.print("</CurrentVolume>");
1277 return written;
1278 },
1279 ref);
1280 },
1281 200, nullptr, this);
1282 return true;
1283 }
1284
1286 void setupRules() {
1287 rules.push_back({"ListPresets", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1288 return self->processActionListPresets(action, server, client);
1289 }});
1290 rules.push_back({"Play", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1291 return self->processActionPlay(action, server, client);
1292 }});
1293 rules.push_back({"Pause", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1294 return self->processActionPause(action, server, client);
1295 }});
1296 rules.push_back({"Stop", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1297 return self->processActionStop(action, server, client);
1298 }});
1299 rules.push_back({"GetCurrentTransportActions", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1300 return self->processActionGetCurrentTransportActions(action, server, client);
1301 }});
1302 rules.push_back({"GetTransportInfo", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1303 return self->processActionGetTransportInfo(action, server, client);
1304 }});
1305 rules.push_back({"GetTransportSettings", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1306 return self->processActionGetTransportSettings(action, server, client);
1307 }});
1308 rules.push_back({"GetPositionInfo", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1309 return self->processActionGetPositionInfo(action, server, client);
1310 }});
1311 rules.push_back({"GetMediaInfo", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1312 return self->processActionGetMediaInfo(action, server, client);
1313 }});
1314 rules.push_back({"GetDeviceCapabilities", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1315 return self->processActionGetDeviceCapabilities(action, server, client);
1316 }});
1317 rules.push_back({"SetAVTransportURI", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1318 return self->processActionSetAVTransportURI(action, server, client);
1319 }});
1320 rules.push_back({"SetVolume", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1321 return self->processActionSetVolume(action, server, client);
1322 }});
1323 rules.push_back({"SetMute", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1324 return self->processActionSetMute(action, server, client);
1325 }});
1326 rules.push_back({"GetMute", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1327 return self->processActionGetMute(action, server, client);
1328 }});
1329 rules.push_back({"GetVolume", [](DLNAMediaRenderer* self, ActionRequest& action, IHttpServer& server, IClientHandler& client) {
1330 return self->processActionGetVolume(action, server, client);
1331 }});
1332 // ConnectionManager rules
1333 rules.push_back(
1334 {"GetProtocolInfo", [](DLNAMediaRenderer* self, ActionRequest& action,
1335 IHttpServer& server, IClientHandler& client) {
1336 return self->processActionGetProtocolInfo(action, server, client);
1337 }});
1338 rules.push_back({"GetCurrentConnectionIDs",
1339 [](DLNAMediaRenderer* self, ActionRequest& action,
1340 IHttpServer& server, IClientHandler& client) {
1342 action, server, client);
1343 }});
1344 rules.push_back({"GetCurrentConnectionInfo",
1345 [](DLNAMediaRenderer* self, ActionRequest& action,
1346 IHttpServer& server, IClientHandler& client) {
1348 action, server, client);
1349 }});
1350 }
1351};
1352
1353} // namespace tiny_dlna
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
ConnectionManager service descriptor (SCPD) generator.
Definition: DLNAMediaRendererDescr.h:606
RenderingControl service descriptor (SCPD) generator.
Definition: DLNAMediaRendererDescr.h:407
AVTransport service descriptor (SCPD) generator.
Definition: DLNAMediaRendererDescr.h:19
MediaRenderer DLNA Device.
Definition: DLNAMediaRenderer.h:71
DLNADescr & getControlDescr()
Get the control SCPD descriptor reference.
Definition: DLNAMediaRenderer.h:515
tiny_dlna::Str current_artist
Definition: DLNAMediaRenderer.h:534
void setPlaybackCompleted()
Notify the renderer that playback completed.
Definition: DLNAMediaRenderer.h:390
bool processActionGetPositionInfo(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:1041
void setPossibleRecordStorageMedia(const char *v)
Set possible record storage media (comma-separated list)
Definition: DLNAMediaRenderer.h:146
void setMimeFromDIDL(const char *didl)
Definition: DLNAMediaRenderer.h:637
void setPlayMode(const char *v)
Set current play mode (e.g. NORMAL, REPEAT_ALL, INTRO)
Definition: DLNAMediaRenderer.h:166
size_t getRelativeTimePositionSec()
Get estimated playback position (seconds)
Definition: DLNAMediaRenderer.h:414
void(* MediaEventHandler)(MediaEvent event, DLNAMediaRenderer &renderer)
Definition: DLNAMediaRenderer.h:76
const char * connectionID
Definition: DLNAMediaRenderer.h:548
static void transportDescrCB(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Definition: DLNAMediaRenderer.h:670
void setMime(const char *mime)
Set MIME explicitly (used when DIDL-Lite metadata provides protocolInfo)
Definition: DLNAMediaRenderer.h:628
static void controlDescrCB(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Definition: DLNAMediaRenderer.h:712
void publishAVT()
Definition: DLNAMediaRenderer.h:578
tiny_dlna::Str current_title
Definition: DLNAMediaRenderer.h:533
DLNADescr * p_transportDescr
Definition: DLNAMediaRenderer.h:559
DLNAMediaRenderer()
Default constructor.
Definition: DLNAMediaRenderer.h:86
void setUdpService(IUDPService &udp)
Set the UDP service instance the MediaRenderer should use.
Definition: DLNAMediaRenderer.h:133
DLNAMediaRenderer(IHttpServer &server, IUDPService &udp)
Recommended constructor Construct a MediaRenderer bound to an HTTP server and IUDPService....
Definition: DLNAMediaRenderer.h:106
void setMediaEventHandler(MediaEventHandler cb)
Register application event handler.
Definition: DLNAMediaRenderer.h:214
tiny_dlna::Str current_mime
Definition: DLNAMediaRenderer.h:531
static void eventSubscriptionHandler(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
After the subscription we publish all relevant properties.
Definition: DLNAMediaRenderer.h:761
const char * getCurrentTransportActions()
Get a csv of the valid actions.
Definition: DLNAMediaRenderer.h:446
void addChange(const char *serviceAbbrev, std::function< size_t(Print &, void *)> changeWriter)
serviceAbbrev: AVT, RCS, CMS
Definition: DLNAMediaRenderer.h:571
void publishRCS()
Publish the current RenderingControl state (Volume, Mute)
Definition: DLNAMediaRenderer.h:603
const char * st
Definition: DLNAMediaRenderer.h:543
uint8_t getVolume()
Get current volume (0-100)
Definition: DLNAMediaRenderer.h:217
bool processActionGetProtocolInfo(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:938
bool processActionGetTransportInfo(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:1011
bool play()
Start playback: same as setActive(true)
Definition: DLNAMediaRenderer.h:289
void setActive(bool active)
Set the active state (used by transport callbacks)
Definition: DLNAMediaRenderer.h:175
void setMuted(bool mute)
Set mute state and publish event.
Definition: DLNAMediaRenderer.h:238
DLNAMediaRendererConnectionMgrDescr default_connmgr_desc
Definition: DLNAMediaRenderer.h:562
bool setPlaybackURL(const char *urlStr)
Defines the actual url to play.
Definition: DLNAMediaRenderer.h:332
void setConnectionMgrDescr(DLNADescr &d)
Set the ConnectionManager SCPD descriptor (non-owning reference).
Definition: DLNAMediaRenderer.h:518
tiny_dlna::Str transport_state
Definition: DLNAMediaRenderer.h:542
bool processActionPause(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:887
const char * getTitle()
Title (dc:title) from the DIDL-Lite metadata or empty string.
Definition: DLNAMediaRenderer.h:265
const char * getAlbum()
Album (upnp:album) from the DIDL-Lite metadata or empty string.
Definition: DLNAMediaRenderer.h:272
DLNADescr & getConnectionMgrDescr()
Get the ConnectionManager SCPD descriptor reference.
Definition: DLNAMediaRenderer.h:521
const char * sourceProto
Definition: DLNAMediaRenderer.h:549
const char * currentPlayMode
Definition: DLNAMediaRenderer.h:554
bool processActionSetAVTransportURI(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:1189
uint8_t current_volume
Definition: DLNAMediaRenderer.h:537
bool processActionGetCurrentConnectionInfo(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:966
bool processActionSetMute(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:1228
tiny_dlna::Str current_uri
Definition: DLNAMediaRenderer.h:530
void setCustomActionRule(const char *suffix, bool(*handler)(DLNAMediaRenderer *, ActionRequest &, IHttpServer &))
Defines a custom action rule for the media renderer.
Definition: DLNAMediaRenderer.h:457
void setupServicesImpl(IHttpServer *server)
Register all services (called when HTTP server is set)
Definition: DLNAMediaRenderer.h:818
const char * getTransportState()
Get textual transport state.
Definition: DLNAMediaRenderer.h:275
const char * getCurrentUri()
Access current URI.
Definition: DLNAMediaRenderer.h:259
const char * getCurrentUriMetadata()
Access the metadata (DIDL-Lite XML) as string.
Definition: DLNAMediaRenderer.h:262
bool processActionPlay(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:856
bool play(const char *urlStr)
Start playback of a network resource (returns true on success)
Definition: DLNAMediaRenderer.h:295
void setTransportDescr(DLNADescr &d)
Set the transport SCPD descriptor (non-owning reference).
Definition: DLNAMediaRenderer.h:506
const char * getConnectionID()
Return the currently configured ConnectionID.
Definition: DLNAMediaRenderer.h:473
bool loop(int loopAction=RUN_ALL)
Call this from Arduino loop()
Definition: DLNAMediaRenderer.h:122
const char * sinkProto
Definition: DLNAMediaRenderer.h:550
Vector< ActionRule > rules
Definition: DLNAMediaRenderer.h:555
bool processActionGetCurrentTransportActions(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:914
const char * getSinkProtocols()
Get the current sink ProtocolInfo string.
Definition: DLNAMediaRenderer.h:503
bool processActionListPresets(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:870
void publishCMS()
Publish a minimal ConnectionManager state (CurrentConnectionIDs)
Definition: DLNAMediaRenderer.h:618
bool begin()
Start the underlying DLNA device using the stored server/udp.
Definition: DLNAMediaRenderer.h:113
IUDPService * p_udp_member
Definition: DLNAMediaRenderer.h:547
IDevice & device()
Provides access to the internal DLNA device instance.
Definition: DLNAMediaRenderer.h:278
DLNAMediaRendererTransportDescr default_transport_desc
Definition: DLNAMediaRenderer.h:558
const char * getSourceProtocols()
Get the current source ProtocolInfo string.
Definition: DLNAMediaRenderer.h:500
DLNADescr * p_connmgrDescr
Definition: DLNAMediaRenderer.h:563
void setupConnectionManagerService(IHttpServer *server)
Definition: DLNAMediaRenderer.h:793
DeviceType dlna_device
Definition: DLNAMediaRenderer.h:545
bool stop()
Stop playback.
Definition: DLNAMediaRenderer.h:360
const char * getMime()
Provides the mime from the DIDL or nullptr.
Definition: DLNAMediaRenderer.h:207
bool processActionGetCurrentConnectionIDs(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:952
DLNAMediaRendererControlDescr default_control_desc
Definition: DLNAMediaRenderer.h:560
const char * getPossibleRecordQualityModes() const
Get possible record quality modes.
Definition: DLNAMediaRenderer.h:161
bool processActionGetDeviceCapabilities(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:980
bool isSubscriptionsActive()
Query whether subscription notifications are active.
Definition: DLNAMediaRenderer.h:286
tiny_dlna::Str current_uri_metadata
Definition: DLNAMediaRenderer.h:532
void setConnectionID(const char *id)
Set the active ConnectionID for the connection manager.
Definition: DLNAMediaRenderer.h:470
bool processActionGetTransportSettings(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:1104
bool processActionSetVolume(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:1213
const char * possiblePlaybackStorageMedia
Definition: DLNAMediaRenderer.h:551
ClientType HttpClient
Definition: DLNAMediaRenderer.h:73
tiny_dlna::Str current_album
Definition: DLNAMediaRenderer.h:535
const char * getPossiblePlaybackStorageMedia() const
Get possible playback storage media.
Definition: DLNAMediaRenderer.h:141
void setProtocols(const char *source, const char *sink="")
Define the source protocol info: use csv Default is http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,...
Definition: DLNAMediaRenderer.h:480
uint8_t muted_volume
Definition: DLNAMediaRenderer.h:538
bool processActionGetMute(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:1243
void setVolume(uint8_t vol)
Set volume and publish event (0-100)
Definition: DLNAMediaRenderer.h:220
bool processAction(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Process parsed SOAP ActionRequest and dispatch to appropriate handler.
Definition: DLNAMediaRenderer.h:834
const char * getArtist()
Definition: DLNAMediaRenderer.h:269
bool processActionStop(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:900
const char * usn
Definition: DLNAMediaRenderer.h:544
void setSubscriptionsActive(bool flag)
Enable/disable subscription notifications.
Definition: DLNAMediaRenderer.h:281
void setMetadata(const char *didl)
Store the DIDL-Lite metadata and extract mime, title, artist and album.
Definition: DLNAMediaRenderer.h:650
void setHttpServer(IHttpServer &server)
Set the http server instance the MediaRenderer should use.
Definition: DLNAMediaRenderer.h:125
const char * possibleRecordQualityModes
Definition: DLNAMediaRenderer.h:553
void setControlDescr(DLNADescr &d)
Set the control SCPD descriptor (non-owning reference).
Definition: DLNAMediaRenderer.h:512
unsigned long start_time
Definition: DLNAMediaRenderer.h:539
IHttpServer * p_server
Definition: DLNAMediaRenderer.h:546
void setupTransportService(IHttpServer *server)
Definition: DLNAMediaRenderer.h:780
unsigned long time_sum
Definition: DLNAMediaRenderer.h:540
void setupRules()
Setup the action handling rules.
Definition: DLNAMediaRenderer.h:1286
void publishGetRelativeTimePositionSec()
Publish the RelativeTimePosition property.
Definition: DLNAMediaRenderer.h:420
bool processActionGetMediaInfo(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:1133
const char * getPlayMode() const
Get current play mode.
Definition: DLNAMediaRenderer.h:169
bool isMuted()
Query mute state.
Definition: DLNAMediaRenderer.h:235
bool processActionGetVolume(ActionRequest &action, IHttpServer &server, IClientHandler &client)
Definition: DLNAMediaRenderer.h:1263
void end()
Stops processing and releases resources.
Definition: DLNAMediaRenderer.h:116
const char * possibleRecordStorageMedia
Definition: DLNAMediaRenderer.h:552
bool isActive()
Query whether renderer is active (playing)
Definition: DLNAMediaRenderer.h:172
DLNADescr & getTransportDescr()
Get the transport SCPD descriptor reference.
Definition: DLNAMediaRenderer.h:509
const char * getPossibleRecordStorageMedia() const
Get possible record storage media.
Definition: DLNAMediaRenderer.h:151
static void connmgrDescrCB(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Definition: DLNAMediaRenderer.h:691
DLNADescr * p_controlDescr
Definition: DLNAMediaRenderer.h:561
static DLNAMediaRenderer * getRenderer(IHttpServer *server)
Determine the Renderer with the help of the server.
Definition: DLNAMediaRenderer.h:754
void setPossiblePlaybackStorageMedia(const char *v)
Set possible playback storage media (comma-separated list)
Definition: DLNAMediaRenderer.h:136
void setPossibleRecordQualityModes(const char *v)
Set possible record quality modes (comma-separated list)
Definition: DLNAMediaRenderer.h:156
void setupRenderingControlService(IHttpServer *server)
Definition: DLNAMediaRenderer.h:805
MediaEventHandler event_cb
Definition: DLNAMediaRenderer.h:536
static void controlCB(IClientHandler &client, IHttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
Definition: DLNAMediaRenderer.h:734
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
const char * get(const char *key)
Definition: HttpHeader.h:161
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
Individual action rule for handling specific actions.
Definition: DLNAMediaRenderer.h:525
const char * suffix
Definition: DLNAMediaRenderer.h:526
bool(* handler)(DLNAMediaRenderer *, ActionRequest &, IHttpServer &, IClientHandler &)
Definition: DLNAMediaRenderer.h:527
Print wrapper that escapes & < > " ' while forwarding to an underlying Print. Returns the expanded ou...
Definition: EscapingPrint.h:9