Arduino DLNA Server
Loading...
Searching...
No Matches
DLNADevice.h
Go to the documentation of this file.
1#pragma once
2
3#include "DLNADeviceInfo.h"
4#include "DLNADevice.h"
6#include "Schedule.h"
7#include "SubscriptionMgr.h"
8#include "basic/Url.h"
9#include "http/HttpServer.h"
10
11namespace tiny_dlna {
12
26 public:
28 bool begin(DLNADeviceInfo& device, IUDPService& udp, HttpServer& server) {
29 DlnaLogger.log(DlnaLogLevel::Info, "DLNADevice::begin");
30
31 p_server = &server;
32 p_udp = &udp;
33 setDevice(device);
35
36 // check base url
37 const char* baseUrl = device.getBaseURL();
38 DlnaLogger.log(DlnaLogLevel::Info, "base URL: %s", baseUrl);
39
40 if (StrView(device.getBaseURL()).contains("localhost")) {
41 DlnaLogger.log(DlnaLogLevel::Error, "invalid base address: %s", baseUrl);
42 return false;
43 }
44
45 // setup device
46 device.setupServices(server, udp);
47 if (!device.begin()) {
48 DlnaLogger.log(DlnaLogLevel::Error, "Device begin failed");
49 return false;
50 }
51
52 // setup web server
53 if (!setupDLNAServer(server)) {
54 DlnaLogger.log(DlnaLogLevel::Error, "setupDLNAServer failed");
55 return false;
56 }
57
58 // start web server
59 Url url{baseUrl};
60 if (!p_server->begin(url.port())) {
61 DlnaLogger.log(DlnaLogLevel::Error, "Server failed");
62 return false;
63 }
64
65 // setup UDP
66 if (!p_udp->begin(DLNABroadcastAddress)) {
67 DlnaLogger.log(DlnaLogLevel::Error, "UDP begin failed");
68 return false;
69 }
70
71 if (!setupScheduler()) {
72 DlnaLogger.log(DlnaLogLevel::Error, "Scheduler failed");
73 return false;
74 }
75
76 is_active = true;
77 DlnaLogger.log(DlnaLogLevel::Info, "Device successfully started");
78 // expose singleton for subscription publishing
79 instance = this;
80 return true;
81 }
82
84 return instance ? &instance->subscriptionMgr : nullptr;
85 }
86
88 void end() {
89 p_server->end();
90
91 // send 3 bye messages
93 bye->repeat_ms = 800;
94 scheduler.add(bye);
95
96 // execute scheduler for 2 seconds
97 uint64_t end = millis() + 2000;
98 while (millis() < end) {
100 }
101
102 is_active = false;
103 }
104
106 bool loop() {
107 if (!is_active) return false;
108
109 // handle server requests
110 bool rc = p_server->doLoop();
111 DlnaLogger.log(DlnaLogLevel::Debug, "server %s", rc ? "true" : "false");
112
113 if (isSchedulerActive()) {
114 // process UDP requests
115 RequestData req = p_udp->receive();
116 if (req) {
117 Schedule* schedule = parser.parse(*p_device, req);
118 if (schedule != nullptr) {
119 scheduler.add(schedule);
120 }
121 }
122
123 // execute scheduled udp replys
125 }
126
127 // be nice, if we have other tasks
128 p_device->loop();
129
130 return true;
131 }
132
134 DLNAServiceInfo getService(const char* id) {
135 return p_device->getService(id);
136 }
137
140
142 void setSchedulerActive(bool flag) { scheduler_active = flag; }
143
146
149 void setPostAliveRepeatMs(uint32_t ms) { post_alive_repeat_ms = ms; }
150
151 protected:
154 IUDPService* p_udp = nullptr;
157 bool is_active = false;
158 bool scheduler_active = true;
161 inline static DLNADevice* instance = nullptr;
162
163 void setDevice(DLNADeviceInfo& device) { p_device = &device; }
164
167 bool setupParser() {
168 parser.addMSearchST("upnp:rootdevice");
169 parser.addMSearchST("ssdp:all");
172 return true;
173 }
174
177 // schedule post alive messages: Usually repeated 2 times (because UDP
178 // messages might be lost)
179 PostAliveSchedule* postAlive =
181 PostAliveSchedule* postAlive1 =
183 postAlive1->time = millis() + 100;
184 scheduler.add(postAlive);
185 scheduler.add(postAlive1);
186 return true;
187 }
188
190 virtual bool setupDLNAServer(HttpServer& srv) {
191 char buffer[DLNA_MAX_URL_LEN] = {0};
192 StrView url(buffer, DLNA_MAX_URL_LEN);
193
194 // add device url to server
195 const char* device_path = p_device->getDeviceURL().path();
196 const char* prefix = p_device->getBaseURL();
197
198 DlnaLogger.log(DlnaLogLevel::Info, "Setting up device path: %s",
199 device_path);
200 void* ref[] = {p_device};
201
202 if (!StrView(device_path).isEmpty()) {
203 p_server->rewrite("/", device_path);
204 p_server->rewrite("/dlna/device.xml", device_path);
205 p_server->rewrite("/index.html", device_path);
206 p_server->on(device_path, T_GET, deviceXMLCallback, ref, 1);
207 }
208
209 // Register icon and privide favicon.ico
210 Icon icon = p_device->getIcon();
211 if (icon.icon_data != nullptr) {
212 char tmp[DLNA_MAX_URL_LEN];
213 // const char* icon_path = url.buildPath(prefix, icon.icon_url);
214 p_server->on(icon.icon_url, T_GET, icon.mime,
215 (const uint8_t*)icon.icon_data, icon.icon_size);
216 p_server->on("/favicon.ico", T_GET, icon.mime,
217 (const uint8_t*)icon.icon_data, icon.icon_size);
218 }
219
220 // Register Service URLs
221 for (DLNAServiceInfo& service : p_device->getServices()) {
222 p_server->on(service.scpd_url, T_GET, service.scp_cb, ref, 1);
223 p_server->on(service.control_url, T_POST, service.control_cb, ref, 1);
224 // register event subscription handler - wrappers for
225 // SUBSCRIBE/UNSUBSCRIBE
226 auto eventHandler = [](HttpServer* server, const char* requestPath,
228 // context[0] contains DLNADevice*
229 DLNADeviceInfo* device = (DLNADeviceInfo*)(hl->context[0]);
230 if (!device) {
231 server->replyNotFound();
232 return;
233 }
234 // header parsing
235 HttpRequestHeader& req = server->requestHeader();
236 // SUBSCRIBE handling
237 if (req.method() == T_SUBSCRIBE) {
238 const char* cb = req.get("CALLBACK");
239 const char* timeout = req.get("TIMEOUT");
240 Str cbStr;
241 if (cb) {
242 cbStr = cb;
243 cbStr.replace("<", "");
244 cbStr.replace(">", "");
245 }
246 uint32_t tsec = 1800;
247 if (timeout && StrView(timeout).startsWith("Second-")) {
248 tsec = atoi(timeout + 7);
249 }
250 // Use the service event path as key for subscriptions
251 const char* svcKey = requestPath;
254 svcKey, cbStr.c_str(), tsec);
255 server->replyHeader().setValues(200, "OK");
256 server->replyHeader().put("SID", sid.c_str());
257 server->replyHeader().put("TIMEOUT", "Second-1800");
258 server->replyHeader().write(server->client());
259 server->endClient();
260 return;
261 }
262 server->replyNotFound();
263 return;
264 }
265 // UNSUBSCRIBE: handle via SID header
266 if (req.method() == T_POST) {
267 // Some stacks use POST for unsubscribe; try to handle SID
268 const char* sid = req.get("SID");
269 if (sid && DLNADevice::instance) {
271 requestPath, sid);
272 if (ok) {
273 server->replyOK();
274 return;
275 }
276 }
277 }
278 // default reply OK
279 server->replyOK();
280 };
281
282 p_server->on(service.event_sub_url, T_GET, eventHandler, ref, 1);
283 }
284
285 return true;
286 }
287
289 static void deviceXMLCallback(HttpServer* server, const char* requestPath,
291 DLNADeviceInfo* device_xml = (DLNADeviceInfo*)(hl->context[0]);
292 assert(device_xml != nullptr);
293 if (device_xml != nullptr) {
294 Client& client = server->client();
295 assert(&client != nullptr);
296 DlnaLogger.log(DlnaLogLevel::Info, "reply %s", "DeviceXML");
297 server->replyHeader().setValues(200, "SUCCESS");
298 server->replyHeader().put(CONTENT_TYPE, "text/xml");
300 server->replyHeader().write(client);
301
302 // print xml result
303 device_xml->print(client);
304 server->endClient();
305 } else {
306 DlnaLogger.log(DlnaLogLevel::Error, "DLNADevice is null");
307 server->replyNotFound();
308 }
309 }
310};
311
312} // namespace tiny_dlna
#define DLNA_MAX_URL_LEN
Definition: DLNAServiceInfo.h:5
Device Attributes and generation of XML using urn:schemas-upnp-org:device-1-0. We could just return a...
Definition: DLNADeviceInfo.h:27
Vector< DLNAServiceInfo > & getServices()
Definition: DLNADeviceInfo.h:150
virtual void loop()
Definition: DLNADeviceInfo.h:187
void print(Print &out)
renderes the device xml
Definition: DLNADeviceInfo.h:40
Icon getIcon(int idx=0)
Definition: DLNADeviceInfo.h:170
DLNAServiceInfo & getService(const char *id)
Finds a service definition by name.
Definition: DLNADeviceInfo.h:140
virtual void setupServices(HttpServer &server, IUDPService &udp)
to be implemented by subclasses
Definition: DLNADeviceInfo.h:218
const char * getUDN()
Provide the udn uuid.
Definition: DLNADeviceInfo.h:56
const char * getBaseURL()
Provides the base url.
Definition: DLNADeviceInfo.h:81
virtual bool begin()
Override to initialize the device.
Definition: DLNADeviceInfo.h:37
Url & getDeviceURL()
This method returns base url/device.xml.
Definition: DLNADeviceInfo.h:92
const char * getDeviceType()
Definition: DLNADeviceInfo.h:50
Translates DLNA UDP Requests to Schedule so that we can schedule a reply.
Definition: DLNADeviceRequestParser.h:15
Schedule * parse(DLNADeviceInfo &device, RequestData &req)
Definition: DLNADeviceRequestParser.h:20
void addMSearchST(const char *accept)
Definition: DLNADeviceRequestParser.h:18
Setup of a Basic DLNA Device service. The device registers itself to the network and answers to the D...
Definition: DLNADevice.h:25
void end()
Stops the processing and releases the resources.
Definition: DLNADevice.h:88
DLNADeviceInfo & getDevice()
Provides the device.
Definition: DLNADevice.h:139
bool setupScheduler()
Schedule PostAlive messages.
Definition: DLNADevice.h:176
void setSchedulerActive(bool flag)
We can activate/deactivate the scheduler.
Definition: DLNADevice.h:142
bool is_active
Definition: DLNADevice.h:157
bool scheduler_active
Definition: DLNADevice.h:158
IUDPService * p_udp
Definition: DLNADevice.h:154
bool begin(DLNADeviceInfo &device, IUDPService &udp, HttpServer &server)
start the
Definition: DLNADevice.h:28
Scheduler scheduler
Definition: DLNADevice.h:152
virtual bool setupDLNAServer(HttpServer &srv)
set up Web Server to handle Service Addresses
Definition: DLNADevice.h:190
DLNADeviceRequestParser parser
Definition: DLNADevice.h:153
bool isSchedulerActive()
Checks if the scheduler is active.
Definition: DLNADevice.h:145
void setPostAliveRepeatMs(uint32_t ms)
Definition: DLNADevice.h:149
static void deviceXMLCallback(HttpServer *server, const char *requestPath, HttpRequestHandlerLine *hl)
callback to provide device XML
Definition: DLNADevice.h:289
uint32_t post_alive_repeat_ms
Definition: DLNADevice.h:159
static DLNADevice * instance
Definition: DLNADevice.h:161
static SubscriptionMgr * getSubscriptionMgr()
Definition: DLNADevice.h:83
HttpServer * p_server
Definition: DLNADevice.h:156
DLNADeviceInfo * p_device
Definition: DLNADevice.h:155
SubscriptionMgr subscriptionMgr
Definition: DLNADevice.h:160
void setDevice(DLNADeviceInfo &device)
Definition: DLNADevice.h:163
bool setupParser()
Definition: DLNADevice.h:167
DLNAServiceInfo getService(const char *id)
Provide addess to the service information.
Definition: DLNADevice.h:134
bool loop()
call this method in the Arduino loop as often as possible
Definition: DLNADevice.h:106
Attributes needed for the DLNA Service Definition.
Definition: DLNAServiceInfo.h:16
TinyMethodID method()
Definition: HttpHeader.h:221
HttpHeader & put(const char *key, const char *value)
Definition: HttpHeader.h:103
void write(Client &out)
Definition: HttpHeader.h:268
const char * get(const char *key)
Definition: HttpHeader.h:163
void setValues(int statusCode, const char *msg="", const char *protocol=nullptr)
Definition: HttpHeader.h:416
Used to register and process callbacks.
Definition: HttpRequestHandlerLine.h:19
void ** context
Definition: HttpRequestHandlerLine.h:39
Reading and writing of Http Requests.
Definition: HttpHeader.h:359
A Simple Header only implementation of Http Server that allows the registration of callback functions...
Definition: HttpServer.h:24
bool begin(int port, const char *ssid, const char *password)
Definition: HttpServer.h:49
bool doLoop()
Legacy method: same as copy();.
Definition: HttpServer.h:404
void on(const char *url, TinyMethodID method, web_callback_fn fn, void *ctx[]=nullptr, int ctxCount=0)
register a generic handler
Definition: HttpServer.h:89
void replyOK()
write OK reply with 200 SUCCESS
Definition: HttpServer.h:363
Client & client()
Provides the current client.
Definition: HttpServer.h:434
void endClient()
closes the connection to the current client_ptr
Definition: HttpServer.h:386
HttpRequestHeader & requestHeader()
provides the request header
Definition: HttpServer.h:380
void end()
stops the server_ptr
Definition: HttpServer.h:76
void replyNotFound()
write 404 reply
Definition: HttpServer.h:366
void rewrite(const char *from, const char *to)
adds a rewrite rule
Definition: HttpServer.h:82
HttpReplyHeader & replyHeader()
provides the reply header
Definition: HttpServer.h:383
Abstract Interface for UDP API.
Definition: IUDPService.h:34
virtual RequestData receive()=0
virtual bool begin(int port)=0
Information about the icon.
Definition: Icon.h:10
const char * icon_url
Definition: Icon.h:16
int icon_size
Definition: Icon.h:18
const char * mime
Definition: Icon.h:12
uint8_t * icon_data
Definition: Icon.h:17
void log(DlnaLogLevel current_level, const char *fmt...)
Print log message.
Definition: Logger.h:40
Send out PostAlive messages: Repeated every 5 seconds.
Definition: Schedule.h:192
Send out ByeBye message.
Definition: Schedule.h:264
Scheduler which processes all due Schedules (to send out UDP replies)
Definition: Scheduler.h:15
void execute(IUDPService &udp)
Execute all due schedules.
Definition: Scheduler.h:25
void add(Schedule *schedule)
Add a schedule to the scheduler.
Definition: Scheduler.h:18
A simple wrapper to provide string functions on char*. If the underlying char* is a const we do not a...
Definition: StrView.h:19
virtual bool startsWith(const char *str)
checks if the string starts with the indicated substring
Definition: StrView.h:184
virtual bool replace(const char *toReplace, const int replaced)
Replaces the first instance of toReplace with replaced.
Definition: StrView.h:395
virtual bool contains(const char *str)
checks if the string contains a substring
Definition: StrView.h:285
String implementation which keeps the data on the heap. We grow the allocated memory only if the copy...
Definition: Str.h:22
const char * c_str()
provides the string value as const char*
Definition: Str.h:187
Manages event subscriptions and notification delivery.
Definition: SubscriptionMgr.h:45
bool unsubscribe(const char *serviceId, const char *sid)
Definition: SubscriptionMgr.h:68
Str subscribe(const char *serviceId, const char *callbackUrl, uint32_t timeoutSec=1800)
Definition: SubscriptionMgr.h:51
URL parser which breaks a full url string up into its individual parts.
Definition: Url.h:18
const char * path()
Definition: Url.h:44
Definition: Allocator.h:6
const char * CONTENT_TYPE
Definition: HttpHeader.h:16
const char * CON_KEEP_ALIVE
Definition: HttpHeader.h:20
@ T_SUBSCRIBE
Definition: HttpHeader.h:46
@ T_GET
Definition: HttpHeader.h:37
@ T_POST
Definition: HttpHeader.h:39
LoggerClass DlnaLogger
Definition: Logger.cpp:5
const char * CONNECTION
Definition: HttpHeader.h:18
Provides information of the received UDP which consists of the (xml) data and the peer address and po...
Definition: IUDPService.h:23
An individual Schedule (to send out UDP messages)
Definition: Schedule.h:17
uint64_t time
Definition: Schedule.h:20
uint32_t repeat_ms
Definition: Schedule.h:22