* Some minor DLNA conformance fixups.

This commit is contained in:
Justin Maggard 2009-05-14 22:13:06 +00:00
parent 5315c89880
commit 5a08b7e275
6 changed files with 116 additions and 49 deletions

View File

@ -202,11 +202,11 @@ GetAudioMetadata(const char * path, char * name)
asprintf(&date, "%04d-01-01", song.year);
sprintf(duration, "%d:%02d:%02d.%03d",
(song.song_length/3600000),
(song.song_length/60000),
(song.song_length/60000%60),
(song.song_length/1000%60),
(song.song_length%1000));
title = song.title;
if( title )
if( title && *title )
{
title = trim(title);
if( (esc_tag = escape_tag(title)) )
@ -221,7 +221,7 @@ GetAudioMetadata(const char * path, char * name)
}
for( i=ROLE_START; i<N_ROLE; i++ )
{
if( song.contributor[i] )
if( song.contributor[i] && *song.contributor[i] )
{
artist = trim(song.contributor[i]);
if( (esc_tag = escape_tag(artist)) )
@ -232,7 +232,7 @@ GetAudioMetadata(const char * path, char * name)
break;
}
}
if( song.album )
if( song.album && *song.album )
{
album = trim(song.album);
if( (esc_tag = escape_tag(album)) )
@ -241,7 +241,7 @@ GetAudioMetadata(const char * path, char * name)
album = esc_tag;
}
}
if( song.genre )
if( song.genre && *song.genre )
{
genre = trim(song.genre);
if( (esc_tag = escape_tag(genre)) )
@ -250,7 +250,7 @@ GetAudioMetadata(const char * path, char * name)
genre = esc_tag;
}
}
if( song.comment )
if( song.comment && *song.comment )
{
comment = trim(song.comment);
if( (esc_tag = escape_tag(comment)) )
@ -438,8 +438,6 @@ GetImageMetadata(const char * path, char * name)
asprintf(&m.dlna_pn, "JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
else if( width <= 4096 && height <= 4096 )
asprintf(&m.dlna_pn, "JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
else
asprintf(&m.dlna_pn, "JPEG_XL");
asprintf(&m.resolution, "%dx%d", width, height);
sql = sqlite3_mprintf( "INSERT into DETAILS"

View File

@ -212,7 +212,7 @@ SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
"DATE: %s\r\n"
"Ext:\r\n"
"ST: %.*s%s\r\n"
"USN: %s::%.*s%s\r\n"
"USN: %s%s%.*s%s\r\n"
"EXT:\r\n"
"SERVER: " MINIDLNA_SERVER_STRING "\r\n"
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
@ -220,7 +220,7 @@ SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
"\r\n",
szTime,
st_len, st, suffix,
uuidvalue, st_len, st, suffix,
uuidvalue, st_len?"::":"", st_len, st, suffix,
host, (unsigned int)port);
n = sendto(s, buf, l, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
@ -232,12 +232,12 @@ SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
static const char * const known_service_types[] =
{
uuidvalue,
"upnp:rootdevice",
"urn:schemas-upnp-org:device:MediaServer:",
"urn:schemas-upnp-org:service:ContentDirectory:",
"urn:schemas-upnp-org:service:ConnectionManager:",
"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
uuidvalue,
0
};
@ -268,14 +268,14 @@ SendSSDPNotifies(int s, const char * host, unsigned short port,
"LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
"SERVER: " MINIDLNA_SERVER_STRING "\r\n"
"NT:%s%s\r\n"
"USN:%s::%s%s\r\n"
"USN:%s%s%s%s\r\n"
"NTS:ssdp:alive\r\n"
"\r\n",
SSDP_MCAST_ADDR, SSDP_PORT,
lifetime,
host, port,
known_service_types[i], (i==0?"":"1"),
uuidvalue, known_service_types[i], (i==0?"":"1") );
known_service_types[i], (i>1?"1":""),
uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
if(l>=sizeof(bufr))
{
DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
@ -399,11 +399,13 @@ ProcessSSDPRequest(int s, unsigned short port)
/* strlen("ssdp:all") == 8 */
if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
{
for(i=0; known_service_types[i]; i++)
SendSSDPAnnounce2(s, sendername, "", 0, "",
lan_addr[lan_addr_index].str, port);
for(i=1; known_service_types[i]; i++)
{
l = (int)strlen(known_service_types[i]);
SendSSDPAnnounce2(s, sendername,
known_service_types[i], l, i==0?"":"1",
known_service_types[i], l, i==1?"":"1",
lan_addr[lan_addr_index].str, port);
}
}
@ -451,19 +453,20 @@ SendSSDPGoodbye(int * sockets, int n_sockets)
"NOTIFY * HTTP/1.1\r\n"
"HOST:%s:%d\r\n"
"NT:%s%s\r\n"
"USN:%s::%s%s\r\n"
"USN:%s%s%s%s\r\n"
"NTS:ssdp:byebye\r\n"
"\r\n",
SSDP_MCAST_ADDR, SSDP_PORT,
known_service_types[i], (i==0?"":"1"),
uuidvalue, known_service_types[i], (i==0?"":"1"));
known_service_types[i], (i>1?"1":""),
uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
//DEBUG printf("Sending NOTIFY:\n%s", bufr);
n = sendto(sockets[j], bufr, l, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
if(n < 0)
{
DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno));
return -1;
}
if(n < 0)
{
DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno));
return -1;
}
}
}
return 0;

View File

@ -127,22 +127,22 @@ static const struct XMLElt rootDesc[] =
{"/mimetype", "image/png"},
{"/width", "48"},
{"/height", "48"},
{"/depth", "32"},
{"/depth", "24"},
{"/url", "/icons/sm.png"},
{"/mimetype", "image/png"},
{"/width", "120"},
{"/height", "120"},
{"/depth", "32"},
{"/depth", "24"},
{"/url", "/icons/lrg.png"},
{"/mimetype", "image/jpeg"},
{"/width", "48"},
{"/height", "48"},
{"/depth", "32"},
{"/depth", "24"},
{"/url", "/icons/sm.jpg"},
{"/mimetype", "image/jpeg"},
{"/width", "120"},
{"/height", "120"},
{"/depth", "32"},
{"/depth", "24"},
{"/url", "/icons/lrg.jpg"},
{"service", INITHELPER(46,5)},
{"service", INITHELPER(51,5)},

View File

@ -22,26 +22,18 @@
#define DB_VERSION 1
#define RESOURCE_PROTOCOL_INFO_VALUES \
"http-get:*:image/jpeg:*," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/avi:*," \
"http-get:*:video/divx:*," \
"http-get:*:video/x-matroska:*," \
"http-get:*:video/mpeg:*," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mp4:*," \
"http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_HD_NA;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/x-ms-wmv:*," \
"http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/x-msvideo:*," \
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01," \
"http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01," \
"http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01," \
@ -49,6 +41,18 @@
"http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01," \
"http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01," \
"http-get:*:audio/mp4:DLNA.ORG_PN=AAC_MULT5_ISO;DLNA.ORG_OP=01," \
"http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01," \
"http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01," \
"http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01," \
"http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01," \
"http-get:*:image/jpeg:*," \
"http-get:*:video/avi:*," \
"http-get:*:video/divx:*," \
"http-get:*:video/x-matroska:*," \
"http-get:*:video/mpeg:*," \
"http-get:*:video/mp4:*," \
"http-get:*:video/x-ms-wmv:*," \
"http-get:*:video/x-msvideo:*," \
"http-get:*:audio/mp4:*," \
"http-get:*:audio/wav:*," \
"http-get:*:audio/x-flac:*," \

View File

@ -649,12 +649,15 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
DPRINTF(E_WARN, L_HTTP, "Invalid request, responding ERROR 400. (No Host specified in HTTP headers?)\n");
Send400(h);
}
else if( (h->reqflags & FLAG_TIMESEEK) || (h->reqflags & FLAG_PLAYSPEED) )
#if 1 /* 7.3.33.4 */
else if( ((h->reqflags & FLAG_TIMESEEK) || (h->reqflags & FLAG_PLAYSPEED)) &&
!(h->reqflags & FLAG_RANGE) )
{
DPRINTF(E_WARN, L_HTTP, "DLNA %s requested, responding ERROR 406\n",
h->reqflags&FLAG_TIMESEEK ? "TimeSeek" : "PlaySpeed");
Send406(h);
}
#endif
else if(strcmp("GET", HttpCommand) == 0)
{
h->req_command = EGet;
@ -1544,7 +1547,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
"Connection: close\r\n"
"Date: %s\r\n"
"EXT:\r\n"
"contentFeatures.dlna.org: DLNA.ORG_PN=%s\r\n"
"contentFeatures.dlna.org: %s\r\n"
"Server: " MINIDLNA_SERVER_STRING "\r\n\r\n",
date, last_file.dlna);
strcat(header, hdr_buf);

View File

@ -191,13 +191,11 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
"xmlns:u=\"%s\">"
"<RcsID>-1</RcsID>"
"<AVTransportID>-1</AVTransportID>"
"<ProtocolInfo>"
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,"
"</ProtocolInfo>"
"<PeerConnectionManager>0</PeerConnectionManager>"
"<ProtocolInfo></ProtocolInfo>"
"<PeerConnectionManager></PeerConnectionManager>"
"<PeerConnectionID>-1</PeerConnectionID>"
"<Direction>0</Direction>"
"<Status>0</Status>"
"<Direction>Output</Direction>"
"<Status>Unknown</Status>"
"</u:%sResponse>";
char body[sizeof(resp)+128];
@ -329,6 +327,7 @@ set_filter_flags(char * filter)
}
else if( strcmp(item, "upnp:albumArtURI@dlna:profileID") == 0 )
{
flags |= FILTER_UPNP_ALBUMARTURI;
flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID;
}
else if( strcmp(item, "upnp:artist") == 0 )
@ -396,11 +395,12 @@ set_filter_flags(char * filter)
}
char *
parse_sort_criteria(char * sortCriteria)
parse_sort_criteria(char * sortCriteria, int * error)
{
char *order = NULL;
char *item, *saveptr;
int i, ret, reverse, title_sorted = 0;
*error = 0;
if( !sortCriteria )
return NULL;
@ -444,6 +444,7 @@ parse_sort_criteria(char * sortCriteria)
else
{
printf("Unhandled SortCriteria [%s]\n", item);
*error = 1;
if( i )
{
ret = strlen(order);
@ -842,9 +843,16 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
sqlite3_free_table(result);
}
#ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */
ret = 0;
if( totalMatches < 10000 )
#endif
orderBy = parse_sort_criteria(SortCriteria);
orderBy = parse_sort_criteria(SortCriteria, &ret);
/* If it's a DLNA client, return an error for bad sort criteria */
if( (args.flags & FLAG_DLNA) && ret )
{
SoapError(h, 709, "Unsupported or invalid sort criteria");
goto browse_error;
}
sql = sqlite3_mprintf( SELECT_COLUMNS
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
@ -859,6 +867,23 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
/* Does the object even exist? */
if( !totalMatches )
{
ret = 0;
sql = sqlite3_mprintf("SELECT count(*) from OBJECTS where OBJECT_ID = '%q'", ObjectId);
if( sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK )
{
ret = atoi(result[1]);
sqlite3_free_table(result);
}
sqlite3_free(sql);
if( !ret )
{
SoapError(h, 701, "No such object error");
goto browse_error;
}
}
ret = snprintf(str_buf, sizeof(str_buf), "&lt;/DIDL-Lite&gt;</Result>\n"
"<NumberReturned>%u</NumberReturned>\n"
"<TotalMatches>%u</TotalMatches>\n"
@ -868,6 +893,7 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret;
BuildSendAndCloseSoapResp(h, resp, args.size);
browse_error:
ClearNameValueList(&data);
if( orderBy )
free(orderBy);
@ -959,7 +985,7 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
}
#endif
}
DPRINTF(E_DEBUG, L_HTTP, "Browsing ContentDirectory:\n"
DPRINTF(E_DEBUG, L_HTTP, "Searching ContentDirectory:\n"
" * ObjectID: %s\n"
" * Count: %d\n"
" * StartingIndex: %d\n"
@ -1011,14 +1037,46 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
ContainerID, SearchCriteria, ContainerID, SearchCriteria);
//DEBUG DPRINTF(E_DEBUG, L_HTTP, "Count SQL: %s\n", sql);
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
if( ret == SQLITE_OK ) {
if( ret == SQLITE_OK )
{
totalMatches = atoi(result[1]);
sqlite3_free_table(result);
}
else
{
/* Must be invalid SQL, so most likely bad or unhandled search criteria. */
SoapError(h, 708, "Unsupported or invalid search criteria");
goto search_error;
}
/* Does the object even exist? */
if( !totalMatches )
{
ret = 0;
sql = sqlite3_mprintf("SELECT count(*) from OBJECTS where OBJECT_ID = '%q'",
!strcmp(ContainerID, "*")?"0":ContainerID);
if( sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK )
{
ret = atoi(result[1]);
sqlite3_free_table(result);
}
sqlite3_free(sql);
if( !ret )
{
SoapError(h, 710, "No such container");
goto search_error;
}
}
#ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */
ret = 0;
if( totalMatches < 10000 )
#endif
orderBy = parse_sort_criteria(SortCriteria);
orderBy = parse_sort_criteria(SortCriteria, &ret);
/* If it's a DLNA client, return an error for bad sort criteria */
if( (args.flags & FLAG_DLNA) && ret )
{
SoapError(h, 709, "Unsupported or invalid sort criteria");
goto search_error;
}
sql = sqlite3_mprintf( SELECT_COLUMNS
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
@ -1049,6 +1107,7 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret;
BuildSendAndCloseSoapResp(h, resp, args.size);
search_error:
ClearNameValueList(&data);
if( orderBy )
free(orderBy);