* Major optimization for large Search/Browse responses. Using strcat gets very slow when the data size gets too big. Switching to memcpy gives us a huge performance improvement with very large responses.

This commit is contained in:
Justin Maggard 2009-04-01 20:19:00 +00:00
parent 047b9511a6
commit 8d12ee4ae7
2 changed files with 285 additions and 157 deletions

View File

@ -203,7 +203,80 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
BuildSendAndCloseSoapResp(h, body, bodylen);
}
static int callback(void *args, int argc, char **argv, char **azColName)
#define FILTER_CHILDCOUNT 0x00000001
#define FILTER_DC_CREATOR 0x00000002
#define FILTER_DC_DATE 0x00000004
#define FILTER_DC_DESCRIPTION 0x00000008
#define FILTER_DLNA_NAMESPACE 0x00000010
#define FILTER_REFID 0x00000020
#define FILTER_RES 0x00000040
#define FILTER_RES_BITRATE 0x00000080
#define FILTER_RES_DURATION 0x00000100
#define FILTER_RES_NRAUDIOCHANNELS 0x00000200
#define FILTER_RES_RESOLUTION 0x00000400
#define FILTER_RES_SAMPLEFREQUENCY 0x00000800
#define FILTER_RES_SIZE 0x00001000
#define FILTER_UPNP_ALBUM 0x00002000
#define FILTER_UPNP_ALBUMARTURI 0x00004000
#define FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID 0x00008000
#define FILTER_UPNP_ARTIST 0x00010000
#define FILTER_UPNP_GENRE 0x00020000
#define FILTER_UPNP_ORIGINALTRACKNUMBER 0x00040000
#define FILTER_UPNP_SEARCHCLASS 0x00080000
static u_int32_t
set_filter_flags(char * filter)
{
u_int32_t flags = 0;
if( !filter || (strlen(filter) <= 1) )
return 0xFFFFFFFF;
if( strstr(filter, "@childCount") )
flags |= FILTER_CHILDCOUNT;
if( strstr(filter, "dc:creator") )
flags |= FILTER_DC_CREATOR;
if( strstr(filter, "dc:date") )
flags |= FILTER_DC_DATE;
if( strstr(filter, "dc:description") )
flags |= FILTER_DC_DESCRIPTION;
if( strstr(filter, "dlna") )
flags |= FILTER_DLNA_NAMESPACE;
if( strstr(filter, "@refID") )
flags |= FILTER_REFID;
if( strstr(filter, "res") )
flags |= FILTER_RES;
if( strstr(filter, "res@bitrate") )
flags |= FILTER_RES_BITRATE;
if( strstr(filter, "res@duration") )
flags |= FILTER_RES_DURATION;
if( strstr(filter, "res@nrAudioChannels") )
flags |= FILTER_RES_NRAUDIOCHANNELS;
if( strstr(filter, "res@resolution") )
flags |= FILTER_RES_RESOLUTION;
if( strstr(filter, "res@sampleFrequency") )
flags |= FILTER_RES_SAMPLEFREQUENCY;
if( strstr(filter, "res@size") )
flags |= FILTER_RES_SIZE;
if( strstr(filter, "upnp:album") )
flags |= FILTER_UPNP_ALBUM;
if( strstr(filter, "upnp:albumArtURI") )
flags |= FILTER_UPNP_ALBUMARTURI;
if( strstr(filter, "upnp:albumArtURI@dlna:profileID") )
flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID;
if( strstr(filter, "upnp:artist") )
flags |= FILTER_UPNP_ARTIST;
if( strstr(filter, "upnp:genre") )
flags |= FILTER_UPNP_GENRE;
if( strstr(filter, "upnp:originalTrackNumber") )
flags |= FILTER_UPNP_ORIGINALTRACKNUMBER;
if( strstr(filter, "upnp:searchClass") )
flags |= FILTER_UPNP_SEARCHCLASS;
return flags;
}
static int
callback(void *args, int argc, char **argv, char **azColName)
{
struct Response *passed_args = (struct Response *)args;
char *id = argv[1], *parent = argv[2], *refID = argv[3], *class = argv[4], *detailID = argv[5], *size = argv[9], *title = argv[10],
@ -211,14 +284,22 @@ static int callback(void *args, int argc, char **argv, char **azColName)
*genre = argv[16], *comment = argv[17], *nrAudioChannels = argv[18], *track = argv[19], *date = argv[20], *resolution = argv[21],
*tn = argv[22], *creator = argv[23], *dlna_pn = argv[24], *mime = argv[25], *album_art = argv[26], *art_dlna_pn = argv[27];
char dlna_buf[64];
char str_buf[4096];
char str_buf[512];
char **result;
int ret;
int children, ret = 0;
static int warned = 0;
passed_args->total++;
if( passed_args->requested && (passed_args->returned >= passed_args->requested) )
return 0;
/* Make sure we have at least 4KB left of allocated memory to finish the response. */
if( passed_args->size > 1044480 && !warned )
{
DPRINTF(E_ERROR, L_HTTP, "UPnP SOAP response is getting too big! [%d returned]\n", passed_args->returned);
warned = 1;
return 0;
}
passed_args->returned++;
if( dlna_pn )
@ -238,92 +319,115 @@ static int callback(void *args, int argc, char **argv, char **azColName)
mime[9] = '\0';
}
}
sprintf(str_buf, "&lt;item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
strcat(passed_args->resp, str_buf);
if( refID && (!passed_args->filter || strstr(passed_args->filter, "@refID")) ) {
sprintf(str_buf, " refID=\"%s\"", refID);
strcat(passed_args->resp, str_buf);
ret = sprintf(str_buf, "&lt;item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
if( refID && (passed_args->filter & FILTER_REFID) ) {
ret = sprintf(str_buf, " refID=\"%s\"", refID);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
sprintf(str_buf, "&gt;"
"&lt;dc:title&gt;%s&lt;/dc:title&gt;"
"&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
title, class);
strcat(passed_args->resp, str_buf);
if( comment && (!passed_args->filter || strstr(passed_args->filter, "dc:description")) ) {
sprintf(str_buf, "&lt;dc:description&gt;%s&lt;/dc:description&gt;", comment);
strcat(passed_args->resp, str_buf);
ret = sprintf(str_buf, "&gt;"
"&lt;dc:title&gt;%s&lt;/dc:title&gt;"
"&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
title, class);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
if( comment && (passed_args->filter & FILTER_DC_DESCRIPTION) ) {
ret = sprintf(str_buf, "&lt;dc:description&gt;%s&lt;/dc:description&gt;", comment);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
sprintf(str_buf, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator);
strcat(passed_args->resp, str_buf);
if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
ret = sprintf(str_buf, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( date && (!passed_args->filter || strstr(passed_args->filter, "dc:date")) ) {
sprintf(str_buf, "&lt;dc:date&gt;%s&lt;/dc:date&gt;", date);
strcat(passed_args->resp, str_buf);
if( date && (passed_args->filter & FILTER_DC_DATE) ) {
ret = sprintf(str_buf, "&lt;dc:date&gt;%s&lt;/dc:date&gt;", date);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
strcat(passed_args->resp, str_buf);
if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) {
ret = sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( album && (!passed_args->filter || strstr(passed_args->filter, "upnp:album")) ) {
sprintf(str_buf, "&lt;upnp:album&gt;%s&lt;/upnp:album&gt;", album);
strcat(passed_args->resp, str_buf);
if( album && (passed_args->filter & FILTER_UPNP_ALBUM) ) {
ret = sprintf(str_buf, "&lt;upnp:album&gt;%s&lt;/upnp:album&gt;", album);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
strcat(passed_args->resp, str_buf);
if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
ret = sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( track && atoi(track) && (!passed_args->filter || strstr(passed_args->filter, "upnp:originalTrackNumber")) ) {
sprintf(str_buf, "&lt;upnp:originalTrackNumber&gt;%s&lt;/upnp:originalTrackNumber&gt;", track);
strcat(passed_args->resp, str_buf);
if( track && atoi(track) && (passed_args->filter & FILTER_UPNP_ORIGINALTRACKNUMBER) ) {
ret = sprintf(str_buf, "&lt;upnp:originalTrackNumber&gt;%s&lt;/upnp:originalTrackNumber&gt;", track);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
strcat(passed_args->resp, "&lt;upnp:albumArtURI ");
if( !passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID") ) {
sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
strcat(passed_args->resp, str_buf);
if( album_art && atoi(album_art) && (passed_args->filter & FILTER_UPNP_ALBUMARTURI) ) {
ret = sprintf(str_buf, "&lt;upnp:albumArtURI ");
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
ret = sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
sprintf(str_buf, "&gt;http://%s:%d/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;",
ret = sprintf(str_buf, "&gt;http://%s:%d/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;",
lan_addr[0].str, runtime_vars.port, album_art);
strcat(passed_args->resp, str_buf);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( !passed_args->filter || strstr(passed_args->filter, "res") ) {
strcat(passed_args->resp, "&lt;res ");
if( size && (!passed_args->filter || strstr(passed_args->filter, "res@size")) ) {
sprintf(str_buf, "size=\"%s\" ", size);
strcat(passed_args->resp, str_buf);
if( passed_args->filter & FILTER_RES ) {
ret = sprintf(str_buf, "&lt;res ");
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
if( size && (passed_args->filter & FILTER_RES_SIZE) ) {
ret = sprintf(str_buf, "size=\"%s\" ", size);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( duration && (!passed_args->filter || strstr(passed_args->filter, "res@duration")) ) {
sprintf(str_buf, "duration=\"%s\" ", duration);
strcat(passed_args->resp, str_buf);
if( duration && (passed_args->filter & FILTER_RES_DURATION) ) {
ret = sprintf(str_buf, "duration=\"%s\" ", duration);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( bitrate && (!passed_args->filter || strstr(passed_args->filter, "res@bitrate")) ) {
if( bitrate && (passed_args->filter & FILTER_RES_BITRATE) ) {
if( passed_args->client == EXbox )
sprintf(str_buf, "bitrate=\"%d\" ", atoi(bitrate)/1024);
ret = sprintf(str_buf, "bitrate=\"%d\" ", atoi(bitrate)/1024);
else
sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
strcat(passed_args->resp, str_buf);
ret = sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( sampleFrequency && (!passed_args->filter || strstr(passed_args->filter, "res@sampleFrequency")) ) {
sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
strcat(passed_args->resp, str_buf);
if( sampleFrequency && (passed_args->filter & FILTER_RES_SAMPLEFREQUENCY) ) {
ret = sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( nrAudioChannels && (!passed_args->filter || strstr(passed_args->filter, "res@nrAudioChannels")) ) {
sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
strcat(passed_args->resp, str_buf);
if( nrAudioChannels && (passed_args->filter & FILTER_RES_NRAUDIOCHANNELS) ) {
ret = sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( resolution && (!passed_args->filter || strstr(passed_args->filter, "res@resolution")) ) {
sprintf(str_buf, "resolution=\"%s\" ", resolution);
strcat(passed_args->resp, str_buf);
if( resolution && (passed_args->filter & FILTER_RES_RESOLUTION) ) {
ret = sprintf(str_buf, "resolution=\"%s\" ", resolution);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/MediaItems/%s.dat"
"&lt;/res&gt;",
mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID);
ret = sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/MediaItems/%s.dat"
"&lt;/res&gt;",
mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID);
#if 0 //JPEG_RESIZE
if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) {
strcat(passed_args->resp, str_buf);
sprintf(str_buf, "&lt;res "
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
ret = sprintf(str_buf, "&lt;res "
"protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/Resized/%s"
"&lt;/res&gt;",
@ -331,70 +435,90 @@ static int callback(void *args, int argc, char **argv, char **azColName)
}
#endif
if( tn && atoi(tn) && dlna_pn ) {
strcat(passed_args->resp, str_buf);
strcat(passed_args->resp, "&lt;res ");
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/Thumbnails/%s.dat"
"&lt;/res&gt;",
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
ret = sprintf(str_buf, "&lt;res protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/Thumbnails/%s.dat"
"&lt;/res&gt;",
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID);
}
strcat(passed_args->resp, str_buf);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
strcpy(str_buf, "&lt;/item&gt;");
ret = sprintf(str_buf, "&lt;/item&gt;");
}
else if( strncmp(class, "container", 9) == 0 )
{
sprintf(str_buf, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id);
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
sprintf(str_buf, "&lt;container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
strcat(passed_args->resp, str_buf);
if( !passed_args->filter || strstr(passed_args->filter, "@childCount"))
ret = sprintf(str_buf, "&lt;container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
if( passed_args->filter & FILTER_CHILDCOUNT )
{
sprintf(str_buf, "childCount=\"%s\"", result[1]);
strcat(passed_args->resp, str_buf);
sprintf(str_buf, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id);
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
if( ret == SQLITE_OK ) {
children = atoi(result[1]);
sqlite3_free_table(result);
}
else {
children = 0;
}
ret = sprintf(str_buf, "childCount=\"%d\"", children);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
/* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s, unless they're filtered out */
if( (passed_args->requested == 1) && (strcmp(id, "0") == 0) )
{
if( !passed_args->filter || strstr(passed_args->filter, "upnp:searchClass") )
if( passed_args->filter & FILTER_UPNP_SEARCHCLASS )
{
strcat(passed_args->resp, "&gt;"
"&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.audioItem&lt;/upnp:searchClass&gt;"
"&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.imageItem&lt;/upnp:searchClass&gt;"
"&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.videoItem&lt;/upnp:searchClass");
ret = sprintf(str_buf, "&gt;"
"&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.audioItem&lt;/upnp:searchClass&gt;"
"&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.imageItem&lt;/upnp:searchClass&gt;"
"&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.videoItem&lt;/upnp:searchClass");
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
}
sprintf(str_buf, "&gt;"
"&lt;dc:title&gt;%s&lt;/dc:title&gt;"
"&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
title, class);
strcat(passed_args->resp, str_buf);
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
sprintf(str_buf, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator);
strcat(passed_args->resp, str_buf);
ret = sprintf(str_buf, "&gt;"
"&lt;dc:title&gt;%s&lt;/dc:title&gt;"
"&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
title, class);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
ret = sprintf(str_buf, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
strcat(passed_args->resp, str_buf);
if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
ret = sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
strcat(passed_args->resp, str_buf);
if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) {
ret = sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
strcat(passed_args->resp, "&lt;upnp:albumArtURI ");
if( !passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID") ) {
sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
strcat(passed_args->resp, str_buf);
if( album_art && atoi(album_art) && (passed_args->filter & FILTER_UPNP_ALBUMARTURI) ) {
ret = sprintf(str_buf, "&lt;upnp:albumArtURI ");
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
ret = sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
sprintf(str_buf, "&gt;http://%s:%d/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;",
ret = sprintf(str_buf, "&gt;http://%s:%d/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;",
lan_addr[0].str, runtime_vars.port, album_art);
strcat(passed_args->resp, str_buf);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
}
sprintf(str_buf, "&lt;/container&gt;");
sqlite3_free_table(result);
ret = sprintf(str_buf, "&lt;/container&gt;");
}
strcat(passed_args->resp, str_buf);
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
return 0;
}
@ -408,11 +532,9 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
"<Result>"
"&lt;DIDL-Lite"
CONTENT_DIRECTORY_SCHEMAS;
static const char resp1[] = "&lt;/DIDL-Lite&gt;</Result>";
static const char resp2[] = "</u:BrowseResponse>";
char *resp = malloc(1048576);
char str_buf[4096];
char str_buf[512];
char *zErrMsg = 0;
char *sql;
int ret;
@ -429,20 +551,25 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria");
if( !ObjectId )
ObjectId = GetValueFromNameValueList(&data, "ContainerID");
memset(str_buf, '\0', sizeof(str_buf));
memset(&args, 0, sizeof(args));
strcpy(resp, resp0);
args.resp = resp;
args.size = sprintf(resp, "%s", resp0);
/* See if we need to include DLNA namespace reference */
if( Filter && ((strlen(Filter) <= 1) || strstr(Filter, "dlna")) )
strcat(resp, DLNA_NAMESPACE);
strcat(resp, "&gt;\n");
args.filter = set_filter_flags(Filter);
if( args.filter & FILTER_DLNA_NAMESPACE )
{
ret = sprintf(str_buf, DLNA_NAMESPACE);
memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret;
}
ret = sprintf(str_buf, "&gt;\n");
memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret;
args.total = StartingIndex;
args.returned = 0;
args.requested = RequestedCount;
args.resp = NULL;
args.filter = NULL;
args.client = h->req_client;
if( h->req_client == EXbox )
{
@ -463,10 +590,6 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
ObjectId, RequestedCount, StartingIndex,
BrowseFlag, Filter, SortCriteria);
if( Filter && (strlen(Filter) > 1) )
args.filter = Filter;
args.resp = resp;
if( strcmp(BrowseFlag, "BrowseMetadata") == 0 )
{
args.requested = 1;
@ -486,14 +609,15 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
strcat(resp, resp1);
sprintf(str_buf, "\n<NumberReturned>%u</NumberReturned>\n"
"<TotalMatches>%u</TotalMatches>\n"
"<UpdateID>%u</UpdateID>",
args.returned, args.total, updateID);
strcat(resp, str_buf);
strcat(resp, resp2);
BuildSendAndCloseSoapResp(h, resp, strlen(resp));
ret = snprintf(str_buf, sizeof(str_buf), "&lt;/DIDL-Lite&gt;</Result>\n"
"<NumberReturned>%u</NumberReturned>\n"
"<TotalMatches>%u</TotalMatches>\n"
"<UpdateID>%u</UpdateID>"
"</u:BrowseResponse>",
args.returned, args.total, updateID);
memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret;
BuildSendAndCloseSoapResp(h, resp, args.size);
ClearNameValueList(&data);
free(resp);
if( h->req_client == EXbox )
@ -511,8 +635,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
"<Result>"
"&lt;DIDL-Lite"
CONTENT_DIRECTORY_SCHEMAS;
static const char resp1[] = "&lt;/DIDL-Lite&gt;</Result>";
static const char resp2[] = "</u:SearchResponse>";
char *resp = malloc(1048576);
char *zErrMsg = 0;
@ -534,11 +656,23 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
memset(&args, 0, sizeof(args));
args.total = 0;
args.resp = resp;
args.size = sprintf(resp, "%s", resp0);
/* See if we need to include DLNA namespace reference */
args.filter = set_filter_flags(Filter);
if( args.filter & FILTER_DLNA_NAMESPACE )
{
ret = sprintf(str_buf, DLNA_NAMESPACE);
memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret;
}
ret = sprintf(str_buf, "&gt;\n");
memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret;
args.total = StartingIndex;
args.returned = 0;
args.requested = RequestedCount;
args.resp = NULL;
args.filter = NULL;
args.client = h->req_client;
if( h->req_client == EXbox )
{
@ -563,14 +697,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
ContainerID, RequestedCount, StartingIndex,
SearchCriteria, Filter, SortCriteria);
strcpy(resp, resp0);
/* See if we need to include DLNA namespace reference */
if( Filter && ((strlen(Filter) <= 1) || strstr(Filter, "dlna")) )
strcat(resp, DLNA_NAMESPACE);
strcat(resp, "&gt;\n");
if( Filter && (strlen(Filter) > 1) )
args.filter = Filter;
if( strcmp(ContainerID, "0") == 0 )
*ContainerID = '*';
if( !SearchCriteria )
@ -603,7 +729,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
}
DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", SearchCriteria);
args.resp = resp;
sql = sqlite3_mprintf("SELECT distinct * from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
" where OBJECT_ID glob '%s$*' and (%s) group by DETAIL_ID "
"%z"
@ -621,14 +746,16 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
sqlite3_free(zErrMsg);
}
sqlite3_free(sql);
strcat(resp, resp1);
sprintf(str_buf, "\n<NumberReturned>%u</NumberReturned>\n"
"<TotalMatches>%u</TotalMatches>\n"
"<UpdateID>%u</UpdateID>",
args.returned, args.total, updateID);
strcat(resp, str_buf);
strcat(resp, resp2);
BuildSendAndCloseSoapResp(h, resp, strlen(resp));
ret = snprintf(str_buf, sizeof(str_buf), "&lt;/DIDL-Lite&gt;</Result>\n"
"<NumberReturned>%u</NumberReturned>\n"
"<TotalMatches>%u</TotalMatches>\n"
"<UpdateID>%u</UpdateID>"
"</u:SearchResponse>",
args.returned, args.total, updateID);
memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret;
BuildSendAndCloseSoapResp(h, resp, args.size);
ClearNameValueList(&data);
if( newSearchCriteria )
free(newSearchCriteria);

View File

@ -21,7 +21,8 @@ struct Response
int returned;
int requested;
int total;
char *filter;
int size;
u_int32_t filter;
enum clientType client;
};