Skip to content

Commit bb92caf

Browse files
committed
MediaServer corrections
1 parent fbc8bc5 commit bb92caf

File tree

5 files changed

+84
-48
lines changed

5 files changed

+84
-48
lines changed

examples/control-point-media-server/control-point-media-server.ino

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ void setup() {
5050
DlnaLogger.begin(Serial, DlnaLogLevel::Info);
5151

5252
setupWifi();
53-
cpms.set
5453

5554
// Start control point and wait to find relevant devices: 10 min
5655
if (!cpms.begin(http, udp, 1000, 10 * 60 * 1000)) {

examples/device-media-server/device-media-server.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ void setup() {
9595
setupWifi();
9696

9797
// define local base URL based on assigned IP address
98-
mediaServer.setFriendlyName("Arduino Media Server");
98+
mediaServer.setFriendlyName("ArduinoMediaServer");
9999
mediaServer.setBaseURL(WiFi.localIP(), 44757);
100100
mediaServer.setReference(nullptr); // not needed for this example
101101
mediaServer.setPrepareDataCallback(myPrepareData);

src/dlna/DLNADevice.h

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class DLNADevice {
152152
static void eventSubscriptionHandler(HttpServer* server,
153153
const char* requestPath,
154154
HttpRequestHandlerLine* hl) {
155+
DlnaLogger.log(DlnaLogLevel::Debug, "eventSubscriptionHandler");
155156
// context[0] contains DLNADeviceInfo*
156157
DLNADeviceInfo* device = (DLNADeviceInfo*)(hl->context[0]);
157158
if (!device) {
@@ -212,42 +213,44 @@ class DLNADevice {
212213
ActionRequest& action) {
213214
DlnaLogger.log(DlnaLogLevel::Info, "parseActionRequest");
214215

215-
// Read full request body available on the server and parse it
216-
Str soap = server->contentStr();
217-
if (soap.isEmpty()) return;
218-
219216
XMLParserPrint xp;
220217
xp.setExpandEncoded(true);
221-
xp.write((const uint8_t*)soap.c_str(), soap.length());
222218

223219
Str outNodeName;
224220
Vector<Str> outPath;
225221
Str outText;
226222
Str outAttributes;
227-
228-
bool seenAction = false;
223+
bool is_attribute = false;
224+
bool is_action = false;
229225
Str actionName;
230-
231-
while (xp.parse(outNodeName, outPath, outText, outAttributes)) {
232-
// skip SOAP envelope wrappers
233-
if (outNodeName.equals("Envelope") || outNodeName.equals("Body")) {
234-
continue;
235-
}
236-
237-
if (!seenAction) {
238-
// first meaningful node is the action name
239-
actionName = outNodeName;
240-
// store action name in registry to keep pointer stable
241-
action.action = registry.add((char*)actionName.c_str());
242-
seenAction = true;
243-
continue;
244-
}
245-
246-
// subsequent nodes are action arguments: add to ActionRequest
247-
if (!outNodeName.isEmpty()) {
248-
// use registry for argument name to have stable pointer
249-
const char* argName = registry.add((char*)outNodeName.c_str());
250-
action.addArgument(argName, outText.c_str());
226+
char buffer[256];
227+
Client &client = server->client();
228+
229+
while (client.available() > 0) {
230+
size_t len = client.readBytes(buffer, sizeof(buffer));
231+
xp.write((const uint8_t*)buffer, len);
232+
233+
while (xp.parse(outNodeName, outPath, outText, outAttributes)) {
234+
if (is_attribute) {
235+
const char* argName = registry.add((char*)outNodeName.c_str());
236+
action.addArgument(argName, outText.c_str());
237+
238+
continue;
239+
}
240+
if (is_action) {
241+
is_action = false;
242+
is_attribute = true;
243+
action.action = registry.add((char*)outNodeName.c_str());
244+
DlnaLogger.log(DlnaLogLevel::Info, "action: %s", action.action);
245+
continue;
246+
}
247+
// skip SOAP envelope wrappers
248+
if (outNodeName.equals("s:Envelope") || outNodeName.equals("Body")) {
249+
continue;
250+
}
251+
if (outNodeName.equals("s:Body")) {
252+
is_action = true;
253+
}
251254
}
252255
}
253256
}
@@ -270,6 +273,7 @@ class DLNADevice {
270273
/// MSearch requests reply to upnp:rootdevice and the device type defined
271274
/// in the device
272275
bool setupParser() {
276+
DlnaLogger.log(DlnaLogLevel::Debug, "setupParser");
273277
parser.addMSearchST("upnp:rootdevice");
274278
parser.addMSearchST("ssdp:all");
275279
parser.addMSearchST(p_device->getUDN());
@@ -279,6 +283,7 @@ class DLNADevice {
279283

280284
/// Schedule PostAlive messages
281285
bool setupScheduler() {
286+
DlnaLogger.log(DlnaLogLevel::Debug, "setupScheduler");
282287
// schedule post alive messages: Usually repeated 2 times (because UDP
283288
// messages might be lost)
284289
PostAliveSchedule* postAlive =
@@ -293,6 +298,7 @@ class DLNADevice {
293298

294299
/// set up Web Server to handle Service Addresses
295300
virtual bool setupDLNAServer(HttpServer& srv) {
301+
DlnaLogger.log(DlnaLogLevel::Debug, "setupDLNAServer");
296302
char buffer[DLNA_MAX_URL_LEN] = {0};
297303
StrView url(buffer, DLNA_MAX_URL_LEN);
298304

@@ -338,6 +344,8 @@ class DLNADevice {
338344
/// callback to provide device XML
339345
static void deviceXMLCallback(HttpServer* server, const char* requestPath,
340346
HttpRequestHandlerLine* hl) {
347+
DlnaLogger.log(DlnaLogLevel::Debug, "deviceXMLCallback");
348+
341349
DLNADeviceInfo* device_xml = (DLNADeviceInfo*)(hl->context[0]);
342350
assert(device_xml != nullptr);
343351
if (device_xml != nullptr) {

src/dlna/devices/MediaServer/MediaServer.h

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ class MediaServer : public DLNADeviceInfo {
9696
/// Provides access to the http server
9797
HttpServer* getHttpServer() { return p_server; }
9898

99+
/// Provides access to the system update ID
100+
int getSystemUpdateID() { return g_stream_updateID; }
101+
102+
/// Increments and returns the SystemUpdateID
103+
int incrementSystemUpdateID() { return ++g_stream_updateID; }
104+
99105
protected:
100106
static inline MediaServer* p_media_server = nullptr;
101107
const char* st = "urn:schemas-upnp-org:device:MediaServer:1";
@@ -114,7 +120,7 @@ class MediaServer : public DLNADeviceInfo {
114120
const char* g_search_capabiities =
115121
"dc:title,dc:creator,upnp:class,upnp:genre,"
116122
"upnp:album,upnp:artist,upnp:albumArtURI";
117-
const char* g_sort_capabilities = "";
123+
const char* g_sort_capabilities = "dc:title";
118124

119125
void setupServicesImpl(HttpServer* server) {
120126
DlnaLogger.log(DlnaLogLevel::Info, "MediaServer::setupServices");
@@ -153,15 +159,20 @@ class MediaServer : public DLNADeviceInfo {
153159
bool processAction(ActionRequest& action, HttpServer& server) {
154160
DlnaLogger.log(DlnaLogLevel::Info, "processAction");
155161
StrView action_str(action.action);
156-
if (StrView(action.action).equals("Browse")) {
162+
if (action_str.isEmpty()){
163+
DlnaLogger.log(DlnaLogLevel::Error, "Empty action received");
164+
server.replyNotFound();
165+
return false;
166+
}
167+
if (action_str.endsWith("Browse")) {
157168
return processActionBrowse(action, server);
158-
} else if (action_str.equals("Search")) {
169+
} else if (action_str.endsWith("Search")) {
159170
return processActionSearch(action, server);
160-
} else if (action_str.equals("GetSearchCapabilities")) {
171+
} else if (action_str.endsWith("GetSearchCapabilities")) {
161172
return processActionGetSearchCapabilities(action, server);
162-
} else if (action_str.equals("GetSortCapabilities")) {
173+
} else if (action_str.endsWith("GetSortCapabilities")) {
163174
return processActionGetSortCapabilities(action, server);
164-
} else if (action_str.equals("GetSystemUpdateID")) {
175+
} else if (action_str.endsWith("GetSystemUpdateID")) {
165176
return processActionGetSystemUpdateID(action, server);
166177
} else {
167178
DlnaLogger.log(DlnaLogLevel::Error, "Unsupported action: %s",
@@ -231,36 +242,37 @@ class MediaServer : public DLNADeviceInfo {
231242

232243
bool processActionGetSearchCapabilities(ActionRequest& action,
233244
HttpServer& server) {
234-
DlnaLogger.log(DlnaLogLevel::Info, "processActionGetSearchCapabilities");
235245
Str reply_str{replyTemplate()};
236-
reply_str.replaceAll("%2", "ContentDirectory");
237-
reply_str.replaceAll("%1", "ActionResponse");
246+
reply_str.replaceAll("%1", "GetSearchCapabilitiesResponse");
247+
reply_str.replaceAll("%2", "SearchCaps");
238248
reply_str.replaceAll("%3", g_search_capabiities);
239249
server.reply("text/xml", reply_str.c_str());
250+
DlnaLogger.log(DlnaLogLevel::Info, "processActionGetSearchCapabilities: %s", reply_str.c_str());
240251
return true;
241252
}
242253

243254
bool processActionGetSortCapabilities(ActionRequest& action,
244255
HttpServer& server) {
245-
DlnaLogger.log(DlnaLogLevel::Info, "processActionGetSortCapabilities");
246256
Str reply_str{replyTemplate()};
247-
reply_str.replaceAll("%2", "ContentDirectory");
248-
reply_str.replaceAll("%1", "ActionResponse");
257+
reply_str.replaceAll("%2", "SortCaps");
258+
reply_str.replaceAll("%1", "GetSortCapabilitiesResponse");
249259
reply_str.replaceAll("%3", g_sort_capabilities);
250260
server.reply("text/xml", reply_str.c_str());
261+
262+
DlnaLogger.log(DlnaLogLevel::Info, "processActionGetSortCapabilities: %s", reply_str.c_str());
251263
return true;
252264
}
253265

254266
bool processActionGetSystemUpdateID(ActionRequest& action,
255267
HttpServer& server) {
256-
DlnaLogger.log(DlnaLogLevel::Info, "processActionGetSystemUpdateID");
257268
Str reply_str{replyTemplate()};
258269
char update_id_str[80];
259270
sprintf(update_id_str, "%d", g_stream_updateID);
260-
reply_str.replaceAll("%2", "ContentDirectory");
261-
reply_str.replaceAll("%1", "ActionResponse");
271+
reply_str.replaceAll("%2", "Id");
272+
reply_str.replaceAll("%1", "GetSystemUpdateIDResponse");
262273
reply_str.replaceAll("%3", update_id_str);
263274
server.reply("text/xml", reply_str.c_str());
275+
DlnaLogger.log(DlnaLogLevel::Info, "processActionGetSystemUpdateID: %s", reply_str.c_str());
264276
return true;
265277
}
266278

@@ -357,13 +369,13 @@ class MediaServer : public DLNADeviceInfo {
357369
}
358370

359371
/// generic SOAP reply template with placeholders: %1 = ActionResponse, %2 =
360-
/// ServiceName, %3 = inner payload
372+
/// payload tag, %3 = inner payload
361373
static const char* replyTemplate() {
362374
static const char* tpl =
363375
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\""
364376
" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
365377
"<s:Body><u:%1 "
366-
"xmlns:u=\"urn:schemas-upnp-org:service:%2:1\">%3</u:%1></s:Body></"
378+
"xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\"><%2>%3</%2></u:%1></s:Body></"
367379
"s:Envelope>";
368380
return tpl;
369381
}
@@ -436,7 +448,6 @@ class MediaServer : public DLNADeviceInfo {
436448
xml.printNodeEnd(responseName, "u");
437449
xml.printNodeEnd("Body", "s");
438450
xml.printNodeEnd("Envelope", "s");
439-
p_media_server->g_stream_updateID++;
440451
}
441452

442453
// helper: stream DIDL-Lite (escaped) for the provided items to the Print

src/dlna/xml/XMLPrinter.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "Print.h"
44
#include "assert.h"
55
#include <cstring>
6+
#include <cstdarg>
7+
#include <cstdio>
68
#include "basic/StrView.h"
79
#include "basic/Vector.h"
810

@@ -95,6 +97,22 @@ struct XMLPrinter {
9597
return result;
9698
}
9799

100+
/**
101+
* printf-style helper that formats into an internal buffer and writes to the
102+
* configured Print output.
103+
*/
104+
size_t printf(const char* fmt, ...) {
105+
assert(p_out != nullptr);
106+
char buf[512];
107+
va_list ap;
108+
va_start(ap, fmt);
109+
int n = ::vsnprintf(buf, sizeof(buf), fmt, ap);
110+
va_end(ap);
111+
if (n <= 0) return 0;
112+
if (n >= (int)sizeof(buf)) n = (int)sizeof(buf) - 1;
113+
return p_out->print(buf);
114+
}
115+
98116
/**
99117
* Helper to print a UPnP <argument> element with name, direction and optional
100118
* relatedStateVariable.

0 commit comments

Comments
 (0)