* Some minor DLNA conformance fixups.
This commit is contained in:
parent
5315c89880
commit
5a08b7e275
14
metadata.c
14
metadata.c
@ -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"
|
||||
|
35
minissdp.c
35
minissdp.c
@ -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;
|
||||
|
@ -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)},
|
||||
|
@ -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:*," \
|
||||
|
@ -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);
|
||||
|
81
upnpsoap.c
81
upnpsoap.c
@ -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), "</DIDL-Lite></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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user