* 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); 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; 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], 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], *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]; *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 dlna_buf[64];
char str_buf[4096]; char str_buf[512];
char **result; char **result;
int ret; int children, ret = 0;
static int warned = 0;
passed_args->total++; passed_args->total++;
if( passed_args->requested && (passed_args->returned >= passed_args->requested) ) if( passed_args->requested && (passed_args->returned >= passed_args->requested) )
return 0; 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++; passed_args->returned++;
if( dlna_pn ) if( dlna_pn )
@ -238,92 +319,115 @@ static int callback(void *args, int argc, char **argv, char **azColName)
mime[9] = '\0'; mime[9] = '\0';
} }
} }
sprintf(str_buf, "&lt;item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent); ret = sprintf(str_buf, "&lt;item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
strcat(passed_args->resp, str_buf); memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
if( refID && (!passed_args->filter || strstr(passed_args->filter, "@refID")) ) { passed_args->size += ret;
sprintf(str_buf, " refID=\"%s\"", refID); if( refID && (passed_args->filter & FILTER_REFID) ) {
strcat(passed_args->resp, str_buf); 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;" ret = sprintf(str_buf, "&gt;"
"&lt;dc:title&gt;%s&lt;/dc:title&gt;" "&lt;dc:title&gt;%s&lt;/dc:title&gt;"
"&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;", "&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
title, class); title, class);
strcat(passed_args->resp, str_buf); memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
if( comment && (!passed_args->filter || strstr(passed_args->filter, "dc:description")) ) { passed_args->size += ret;
sprintf(str_buf, "&lt;dc:description&gt;%s&lt;/dc:description&gt;", comment); if( comment && (passed_args->filter & FILTER_DC_DESCRIPTION) ) {
strcat(passed_args->resp, str_buf); 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")) ) { if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
sprintf(str_buf, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator); ret = sprintf(str_buf, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator);
strcat(passed_args->resp, str_buf); 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")) ) { if( date && (passed_args->filter & FILTER_DC_DATE) ) {
sprintf(str_buf, "&lt;dc:date&gt;%s&lt;/dc:date&gt;", date); ret = sprintf(str_buf, "&lt;dc:date&gt;%s&lt;/dc:date&gt;", date);
strcat(passed_args->resp, str_buf); 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")) ) { if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) {
sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist); ret = sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
strcat(passed_args->resp, str_buf); 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")) ) { if( album && (passed_args->filter & FILTER_UPNP_ALBUM) ) {
sprintf(str_buf, "&lt;upnp:album&gt;%s&lt;/upnp:album&gt;", album); ret = sprintf(str_buf, "&lt;upnp:album&gt;%s&lt;/upnp:album&gt;", album);
strcat(passed_args->resp, str_buf); 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")) ) { if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre); ret = sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
strcat(passed_args->resp, str_buf); 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")) ) { if( track && atoi(track) && (passed_args->filter & FILTER_UPNP_ORIGINALTRACKNUMBER) ) {
sprintf(str_buf, "&lt;upnp:originalTrackNumber&gt;%s&lt;/upnp:originalTrackNumber&gt;", track); ret = sprintf(str_buf, "&lt;upnp:originalTrackNumber&gt;%s&lt;/upnp:originalTrackNumber&gt;", track);
strcat(passed_args->resp, str_buf); 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")) ) { if( album_art && atoi(album_art) && (passed_args->filter & FILTER_UPNP_ALBUMARTURI) ) {
strcat(passed_args->resp, "&lt;upnp:albumArtURI "); ret = sprintf(str_buf, "&lt;upnp:albumArtURI ");
if( !passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID") ) { memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn); passed_args->size += ret;
strcat(passed_args->resp, str_buf); 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); 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") ) { if( passed_args->filter & FILTER_RES ) {
strcat(passed_args->resp, "&lt;res "); ret = sprintf(str_buf, "&lt;res ");
if( size && (!passed_args->filter || strstr(passed_args->filter, "res@size")) ) { memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
sprintf(str_buf, "size=\"%s\" ", size); passed_args->size += ret;
strcat(passed_args->resp, str_buf); 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")) ) { if( duration && (passed_args->filter & FILTER_RES_DURATION) ) {
sprintf(str_buf, "duration=\"%s\" ", duration); ret = sprintf(str_buf, "duration=\"%s\" ", duration);
strcat(passed_args->resp, str_buf); 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 ) if( passed_args->client == EXbox )
sprintf(str_buf, "bitrate=\"%d\" ", atoi(bitrate)/1024); ret = sprintf(str_buf, "bitrate=\"%d\" ", atoi(bitrate)/1024);
else else
sprintf(str_buf, "bitrate=\"%s\" ", bitrate); ret = sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
strcat(passed_args->resp, str_buf); 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")) ) { if( sampleFrequency && (passed_args->filter & FILTER_RES_SAMPLEFREQUENCY) ) {
sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency); ret = sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
strcat(passed_args->resp, str_buf); 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")) ) { if( nrAudioChannels && (passed_args->filter & FILTER_RES_NRAUDIOCHANNELS) ) {
sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels); ret = sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
strcat(passed_args->resp, str_buf); 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")) ) { if( resolution && (passed_args->filter & FILTER_RES_RESOLUTION) ) {
sprintf(str_buf, "resolution=\"%s\" ", resolution); ret = sprintf(str_buf, "resolution=\"%s\" ", resolution);
strcat(passed_args->resp, str_buf); memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
} }
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;" ret = sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/MediaItems/%s.dat" "http://%s:%d/MediaItems/%s.dat"
"&lt;/res&gt;", "&lt;/res&gt;",
mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID); mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID);
#if 0 //JPEG_RESIZE #if 0 //JPEG_RESIZE
if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) { if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) {
strcat(passed_args->resp, str_buf); memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
sprintf(str_buf, "&lt;res " passed_args->size += ret;
ret = sprintf(str_buf, "&lt;res "
"protocolInfo=\"http-get:*:%s:%s\"&gt;" "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/Resized/%s" "http://%s:%d/Resized/%s"
"&lt;/res&gt;", "&lt;/res&gt;",
@ -331,70 +435,90 @@ static int callback(void *args, int argc, char **argv, char **azColName)
} }
#endif #endif
if( tn && atoi(tn) && dlna_pn ) { if( tn && atoi(tn) && dlna_pn ) {
strcat(passed_args->resp, str_buf); memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
strcat(passed_args->resp, "&lt;res "); passed_args->size += ret;
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;" ret = sprintf(str_buf, "&lt;res protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/Thumbnails/%s.dat" "http://%s:%d/Thumbnails/%s.dat"
"&lt;/res&gt;", "&lt;/res&gt;",
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID); 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 ) else if( strncmp(class, "container", 9) == 0 )
{
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, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id); sprintf(str_buf, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id);
ret = sql_get_table(db, str_buf, &result, NULL, NULL); ret = sql_get_table(db, str_buf, &result, NULL, NULL);
sprintf(str_buf, "&lt;container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent); if( ret == SQLITE_OK ) {
strcat(passed_args->resp, str_buf); children = atoi(result[1]);
if( !passed_args->filter || strstr(passed_args->filter, "@childCount")) sqlite3_free_table(result);
{ }
sprintf(str_buf, "childCount=\"%s\"", result[1]); else {
strcat(passed_args->resp, str_buf); 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 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->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;" 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.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.imageItem&lt;/upnp:searchClass&gt;"
"&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.videoItem&lt;/upnp:searchClass"); "&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;" ret = sprintf(str_buf, "&gt;"
"&lt;dc:title&gt;%s&lt;/dc:title&gt;" "&lt;dc:title&gt;%s&lt;/dc:title&gt;"
"&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;", "&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
title, class); title, class);
strcat(passed_args->resp, str_buf); memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) { passed_args->size += ret;
sprintf(str_buf, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator); if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
strcat(passed_args->resp, str_buf); 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")) ) { if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre); ret = sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
strcat(passed_args->resp, str_buf); 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")) ) { if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) {
sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist); ret = sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
strcat(passed_args->resp, str_buf); 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")) ) { if( album_art && atoi(album_art) && (passed_args->filter & FILTER_UPNP_ALBUMARTURI) ) {
strcat(passed_args->resp, "&lt;upnp:albumArtURI "); ret = sprintf(str_buf, "&lt;upnp:albumArtURI ");
if( !passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID") ) { memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn); passed_args->size += ret;
strcat(passed_args->resp, str_buf); 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); 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;"); ret = sprintf(str_buf, "&lt;/container&gt;");
sqlite3_free_table(result);
} }
strcat(passed_args->resp, str_buf); memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
passed_args->size += ret;
return 0; return 0;
} }
@ -408,11 +532,9 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
"<Result>" "<Result>"
"&lt;DIDL-Lite" "&lt;DIDL-Lite"
CONTENT_DIRECTORY_SCHEMAS; CONTENT_DIRECTORY_SCHEMAS;
static const char resp1[] = "&lt;/DIDL-Lite&gt;</Result>";
static const char resp2[] = "</u:BrowseResponse>";
char *resp = malloc(1048576); char *resp = malloc(1048576);
char str_buf[4096]; char str_buf[512];
char *zErrMsg = 0; char *zErrMsg = 0;
char *sql; char *sql;
int ret; int ret;
@ -429,20 +551,25 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria"); char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria");
if( !ObjectId ) if( !ObjectId )
ObjectId = GetValueFromNameValueList(&data, "ContainerID"); ObjectId = GetValueFromNameValueList(&data, "ContainerID");
memset(str_buf, '\0', sizeof(str_buf));
memset(&args, 0, sizeof(args)); 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 */ /* See if we need to include DLNA namespace reference */
if( Filter && ((strlen(Filter) <= 1) || strstr(Filter, "dlna")) ) args.filter = set_filter_flags(Filter);
strcat(resp, DLNA_NAMESPACE); if( args.filter & FILTER_DLNA_NAMESPACE )
strcat(resp, "&gt;\n"); {
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.total = StartingIndex;
args.returned = 0; args.returned = 0;
args.requested = RequestedCount; args.requested = RequestedCount;
args.resp = NULL;
args.filter = NULL;
args.client = h->req_client; args.client = h->req_client;
if( h->req_client == EXbox ) if( h->req_client == EXbox )
{ {
@ -463,10 +590,6 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
ObjectId, RequestedCount, StartingIndex, ObjectId, RequestedCount, StartingIndex,
BrowseFlag, Filter, SortCriteria); BrowseFlag, Filter, SortCriteria);
if( Filter && (strlen(Filter) > 1) )
args.filter = Filter;
args.resp = resp;
if( strcmp(BrowseFlag, "BrowseMetadata") == 0 ) if( strcmp(BrowseFlag, "BrowseMetadata") == 0 )
{ {
args.requested = 1; args.requested = 1;
@ -486,14 +609,15 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg); DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg); sqlite3_free(zErrMsg);
} }
strcat(resp, resp1); ret = snprintf(str_buf, sizeof(str_buf), "&lt;/DIDL-Lite&gt;</Result>\n"
sprintf(str_buf, "\n<NumberReturned>%u</NumberReturned>\n" "<NumberReturned>%u</NumberReturned>\n"
"<TotalMatches>%u</TotalMatches>\n" "<TotalMatches>%u</TotalMatches>\n"
"<UpdateID>%u</UpdateID>", "<UpdateID>%u</UpdateID>"
"</u:BrowseResponse>",
args.returned, args.total, updateID); args.returned, args.total, updateID);
strcat(resp, str_buf); memcpy(resp+args.size, &str_buf, ret+1);
strcat(resp, resp2); args.size += ret;
BuildSendAndCloseSoapResp(h, resp, strlen(resp)); BuildSendAndCloseSoapResp(h, resp, args.size);
ClearNameValueList(&data); ClearNameValueList(&data);
free(resp); free(resp);
if( h->req_client == EXbox ) if( h->req_client == EXbox )
@ -511,8 +635,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
"<Result>" "<Result>"
"&lt;DIDL-Lite" "&lt;DIDL-Lite"
CONTENT_DIRECTORY_SCHEMAS; CONTENT_DIRECTORY_SCHEMAS;
static const char resp1[] = "&lt;/DIDL-Lite&gt;</Result>";
static const char resp2[] = "</u:SearchResponse>";
char *resp = malloc(1048576); char *resp = malloc(1048576);
char *zErrMsg = 0; char *zErrMsg = 0;
@ -534,11 +656,23 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
memset(&args, 0, sizeof(args)); 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.returned = 0;
args.requested = RequestedCount; args.requested = RequestedCount;
args.resp = NULL;
args.filter = NULL;
args.client = h->req_client; args.client = h->req_client;
if( h->req_client == EXbox ) if( h->req_client == EXbox )
{ {
@ -563,14 +697,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
ContainerID, RequestedCount, StartingIndex, ContainerID, RequestedCount, StartingIndex,
SearchCriteria, Filter, SortCriteria); 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 ) if( strcmp(ContainerID, "0") == 0 )
*ContainerID = '*'; *ContainerID = '*';
if( !SearchCriteria ) if( !SearchCriteria )
@ -603,7 +729,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
} }
DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", SearchCriteria); 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)" 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 " " where OBJECT_ID glob '%s$*' and (%s) group by DETAIL_ID "
"%z" "%z"
@ -621,14 +746,16 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
sqlite3_free(zErrMsg); sqlite3_free(zErrMsg);
} }
sqlite3_free(sql); 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, str_buf);
strcat(resp, resp2); ret = snprintf(str_buf, sizeof(str_buf), "&lt;/DIDL-Lite&gt;</Result>\n"
BuildSendAndCloseSoapResp(h, resp, strlen(resp)); "<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); ClearNameValueList(&data);
if( newSearchCriteria ) if( newSearchCriteria )
free(newSearchCriteria); free(newSearchCriteria);

View File

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