Lots of changes, but notably:
* MiniDLNA can now pass the DLNA Conformance Test! * Dependence on libdlna has been removed, and the ffmpeg libs are used directly. * Lots of unused code has been cleaned up. * File transfers will now be forked off into a new process, so as not to tie up the server when sending data.
This commit is contained in:
parent
9867def383
commit
74d73037d0
7
Makefile
7
Makefile
@ -12,7 +12,10 @@
|
|||||||
#
|
#
|
||||||
#CFLAGS = -Wall -O -D_GNU_SOURCE -g -DDEBUG
|
#CFLAGS = -Wall -O -D_GNU_SOURCE -g -DDEBUG
|
||||||
#CFLAGS = -Wall -g -Os -D_GNU_SOURCE
|
#CFLAGS = -Wall -g -Os -D_GNU_SOURCE
|
||||||
CFLAGS = -Wall -g -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
|
CFLAGS = -Wall -g -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 \
|
||||||
|
-I/usr/include/ffmpeg \
|
||||||
|
-I/usr/include/libavutil -I/usr/include/libavcodec -I/usr/include/libavformat \
|
||||||
|
-I/usr/include/ffmpeg/libavutil -I/usr/include/ffmpeg/libavcodec -I/usr/include/ffmpeg/libavformat
|
||||||
CC = gcc
|
CC = gcc
|
||||||
RM = rm -f
|
RM = rm -f
|
||||||
INSTALL = install
|
INSTALL = install
|
||||||
@ -30,7 +33,7 @@ BASEOBJS = minidlna.o upnphttp.o upnpdescgen.o upnpsoap.o \
|
|||||||
ALLOBJS = $(BASEOBJS) $(LNXOBJS)
|
ALLOBJS = $(BASEOBJS) $(LNXOBJS)
|
||||||
|
|
||||||
#LIBS = -liptc
|
#LIBS = -liptc
|
||||||
LIBS = -lexif -ltag_c -lsqlite3 -ldlna #-lgd
|
LIBS = -lexif -ltag_c -lsqlite3 -lavformat -luuid #-lgd
|
||||||
|
|
||||||
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
|
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
|
||||||
|
|
||||||
|
7
TODO
7
TODO
@ -1,13 +1,8 @@
|
|||||||
Things left to do:
|
Things left to do:
|
||||||
|
|
||||||
* Clean up more leftover MiniUPnPd code
|
|
||||||
* Spawn a new thread for new HTTP connections
|
|
||||||
* Persistent HTTP connection (Keep-Alive) support
|
* Persistent HTTP connection (Keep-Alive) support
|
||||||
* Real Chunked transfer support
|
|
||||||
* PNG image support
|
* PNG image support
|
||||||
* DLNA Profile Name (DLNA.ORG_PN) support for video files
|
* DLNA Profile Name (DLNA.ORG_PN) support for more audio files
|
||||||
* DLNA Profile Name (DLNA.ORG_PN) support for audio files besides MP3
|
|
||||||
* Video metadata support
|
|
||||||
* SortCriteria support
|
* SortCriteria support
|
||||||
* Completely redo the logging scheme.
|
* Completely redo the logging scheme.
|
||||||
* Update scan support (do not require removing the database)
|
* Update scan support (do not require removing the database)
|
||||||
|
55
config.h
55
config.h
@ -1,55 +0,0 @@
|
|||||||
/* MiniUPnP Project
|
|
||||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
|
||||||
* (c) 2006-2008 Thomas Bernard
|
|
||||||
* generated by ./genconfig.sh on Thu Sep 11 15:05:26 PDT 2008 */
|
|
||||||
#ifndef __CONFIG_H__
|
|
||||||
#define __CONFIG_H__
|
|
||||||
|
|
||||||
#define UPNP_VERSION "20070827"
|
|
||||||
#define USE_NETFILTER 1
|
|
||||||
#define OS_NAME "JM"
|
|
||||||
#define OS_VERSION "Linux/2.6.25.14-108.fc9.i686"
|
|
||||||
#define OS_URL "http://www.kernel.org/"
|
|
||||||
|
|
||||||
/* syslog facility to be used by miniupnpd */
|
|
||||||
#define LOG_MINIUPNPD LOG_DAEMON
|
|
||||||
|
|
||||||
/* Uncomment the following line to allow miniupnpd to be
|
|
||||||
* controlled by miniupnpdctl */
|
|
||||||
/*#define USE_MINIUPNPDCTL*/
|
|
||||||
|
|
||||||
/* Comment the following line to disable NAT-PMP operations */
|
|
||||||
//#define ENABLE_NATPMP
|
|
||||||
|
|
||||||
/* Uncomment the following line to enable generation of
|
|
||||||
* filter rules with pf */
|
|
||||||
/*#define PF_ENABLE_FILTER_RULES*/
|
|
||||||
|
|
||||||
/* Uncomment the following line to enable caching of results of
|
|
||||||
* the getifstats() function */
|
|
||||||
/*#define ENABLE_GETIFSTATS_CACHING*/
|
|
||||||
/* The cache duration is indicated in seconds */
|
|
||||||
#define GETIFSTATS_CACHING_DURATION 2
|
|
||||||
|
|
||||||
/* Uncomment the following line to enable multiple external ip support */
|
|
||||||
/* note : Thas is EXPERIMENTAL, do not use that unless you know perfectly what you are doing */
|
|
||||||
/*#define MULTIPLE_EXTERNAL_IP*/
|
|
||||||
|
|
||||||
/* Comment the following line to use home made daemonize() func instead
|
|
||||||
* of BSD daemon() */
|
|
||||||
#define USE_DAEMON
|
|
||||||
|
|
||||||
/* Uncomment the following line to enable lease file support */
|
|
||||||
/*#define ENABLE_LEASEFILE*/
|
|
||||||
|
|
||||||
/* Define one or none of the two following macros in order to make some
|
|
||||||
* clients happy. It will change the XML Root Description of the IGD.
|
|
||||||
* Enabling the Layer3Forwarding Service seems to be the more compatible
|
|
||||||
* option. */
|
|
||||||
/*#define HAS_DUMMY_SERVICE*/
|
|
||||||
#define ENABLE_L3F_SERVICE
|
|
||||||
|
|
||||||
/* Experimental UPnP Events support. */
|
|
||||||
#define ENABLE_EVENTS
|
|
||||||
|
|
||||||
#endif
|
|
45
genconfig.sh
45
genconfig.sh
@ -13,7 +13,9 @@ CONFIGMACRO="__CONFIG_H__"
|
|||||||
# version reported in XML descriptions
|
# version reported in XML descriptions
|
||||||
UPNP_VERSION=20070827
|
UPNP_VERSION=20070827
|
||||||
# Facility to syslog
|
# Facility to syslog
|
||||||
LOG_MINIUPNPD="LOG_DAEMON"
|
LOG_MINIDLNA="LOG_DAEMON"
|
||||||
|
# Database path
|
||||||
|
DB_PATH="/tmp/files.db"
|
||||||
|
|
||||||
# detecting the OS name and version
|
# detecting the OS name and version
|
||||||
OS_NAME=`uname -s`
|
OS_NAME=`uname -s`
|
||||||
@ -52,7 +54,6 @@ case $OS_NAME in
|
|||||||
if [ \( $MAJORVER -ge 4 \) -o \( $MAJORVER -eq 3 -a $MINORVER -ge 8 \) ]; then
|
if [ \( $MAJORVER -ge 4 \) -o \( $MAJORVER -eq 3 -a $MINORVER -ge 8 \) ]; then
|
||||||
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
|
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
|
||||||
fi
|
fi
|
||||||
echo "#define USE_PF 1" >> ${CONFIGFILE}
|
|
||||||
OS_URL=http://www.openbsd.org/
|
OS_URL=http://www.openbsd.org/
|
||||||
;;
|
;;
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
@ -60,25 +61,14 @@ case $OS_NAME in
|
|||||||
if [ $VER -ge 700049 ]; then
|
if [ $VER -ge 700049 ]; then
|
||||||
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
|
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
|
||||||
fi
|
fi
|
||||||
if [ -f /usr/include/net/pfvar.h ] ; then
|
|
||||||
echo "#define USE_PF 1" >> ${CONFIGFILE}
|
|
||||||
else
|
|
||||||
echo "#define USE_IPF 1" >> ${CONFIGFILE}
|
|
||||||
fi
|
|
||||||
OS_URL=http://www.freebsd.org/
|
OS_URL=http://www.freebsd.org/
|
||||||
;;
|
;;
|
||||||
pfSense)
|
pfSense)
|
||||||
# we need to detect if PFRULE_INOUT_COUNTS macro is needed
|
# we need to detect if PFRULE_INOUT_COUNTS macro is needed
|
||||||
echo "#define USE_PF 1" >> ${CONFIGFILE}
|
|
||||||
OS_URL=http://www.pfsense.com/
|
OS_URL=http://www.pfsense.com/
|
||||||
;;
|
;;
|
||||||
NetBSD)
|
NetBSD)
|
||||||
OS_URL=http://www.netbsd.org/
|
OS_URL=http://www.netbsd.org/
|
||||||
if [ -f /usr/include/net/pfvar.h ] ; then
|
|
||||||
echo "#define USE_PF 1" >> ${CONFIGFILE}
|
|
||||||
else
|
|
||||||
echo "#define USE_IPF 1" >> ${CONFIGFILE}
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
SunOS)
|
SunOS)
|
||||||
echo "#define USE_IPF 1" >> ${CONFIGFILE}
|
echo "#define USE_IPF 1" >> ${CONFIGFILE}
|
||||||
@ -104,7 +94,7 @@ case $OS_NAME in
|
|||||||
OS_URL=http://www.debian.org/
|
OS_URL=http://www.debian.org/
|
||||||
fi
|
fi
|
||||||
# use lsb_release (Linux Standard Base) when available
|
# use lsb_release (Linux Standard Base) when available
|
||||||
LSB_RELEASE=`which lsb_release`
|
LSB_RELEASE=`which lsb_release 2>/dev/null`
|
||||||
if [ 0 -eq $? ]; then
|
if [ 0 -eq $? ]; then
|
||||||
OS_NAME=`${LSB_RELEASE} -i -s`
|
OS_NAME=`${LSB_RELEASE} -i -s`
|
||||||
OS_VERSION=`${LSB_RELEASE} -r -s`
|
OS_VERSION=`${LSB_RELEASE} -r -s`
|
||||||
@ -123,8 +113,12 @@ echo "#define OS_VERSION \"$OS_NAME/$OS_VERSION\"" >> ${CONFIGFILE}
|
|||||||
echo "#define OS_URL \"${OS_URL}\"" >> ${CONFIGFILE}
|
echo "#define OS_URL \"${OS_URL}\"" >> ${CONFIGFILE}
|
||||||
echo "" >> ${CONFIGFILE}
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
|
echo "/* full path of the file database */" >> ${CONFIGFILE}
|
||||||
|
echo "#define DB_PATH \"${DB_PATH}\"" >> ${CONFIGFILE}
|
||||||
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
echo "/* syslog facility to be used by miniupnpd */" >> ${CONFIGFILE}
|
echo "/* syslog facility to be used by miniupnpd */" >> ${CONFIGFILE}
|
||||||
echo "#define LOG_MINIUPNPD ${LOG_MINIUPNPD}" >> ${CONFIGFILE}
|
echo "#define LOG_MINIDLNA ${LOG_MINIDLNA}" >> ${CONFIGFILE}
|
||||||
echo "" >> ${CONFIGFILE}
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
echo "/* Uncomment the following line to allow miniupnpd to be" >> ${CONFIGFILE}
|
echo "/* Uncomment the following line to allow miniupnpd to be" >> ${CONFIGFILE}
|
||||||
@ -132,15 +126,6 @@ echo " * controlled by miniupnpdctl */" >> ${CONFIGFILE}
|
|||||||
echo "/*#define USE_MINIUPNPDCTL*/" >> ${CONFIGFILE}
|
echo "/*#define USE_MINIUPNPDCTL*/" >> ${CONFIGFILE}
|
||||||
echo "" >> ${CONFIGFILE}
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
echo "/* Comment the following line to disable NAT-PMP operations */" >> ${CONFIGFILE}
|
|
||||||
echo "#define ENABLE_NATPMP" >> ${CONFIGFILE}
|
|
||||||
echo "" >> ${CONFIGFILE}
|
|
||||||
|
|
||||||
echo "/* Uncomment the following line to enable generation of" >> ${CONFIGFILE}
|
|
||||||
echo " * filter rules with pf */" >> ${CONFIGFILE}
|
|
||||||
echo "/*#define PF_ENABLE_FILTER_RULES*/">> ${CONFIGFILE}
|
|
||||||
echo "" >> ${CONFIGFILE}
|
|
||||||
|
|
||||||
echo "/* Uncomment the following line to enable caching of results of" >> ${CONFIGFILE}
|
echo "/* Uncomment the following line to enable caching of results of" >> ${CONFIGFILE}
|
||||||
echo " * the getifstats() function */" >> ${CONFIGFILE}
|
echo " * the getifstats() function */" >> ${CONFIGFILE}
|
||||||
echo "/*#define ENABLE_GETIFSTATS_CACHING*/" >> ${CONFIGFILE}
|
echo "/*#define ENABLE_GETIFSTATS_CACHING*/" >> ${CONFIGFILE}
|
||||||
@ -162,16 +147,12 @@ echo "/* Uncomment the following line to enable lease file support */" >> ${CONF
|
|||||||
echo "/*#define ENABLE_LEASEFILE*/" >> ${CONFIGFILE}
|
echo "/*#define ENABLE_LEASEFILE*/" >> ${CONFIGFILE}
|
||||||
echo "" >> ${CONFIGFILE}
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
echo "/* Define one or none of the two following macros in order to make some" >> ${CONFIGFILE}
|
echo "/* Experimental UPnP Events support. */" >> ${CONFIGFILE}
|
||||||
echo " * clients happy. It will change the XML Root Description of the IGD." >> ${CONFIGFILE}
|
echo "#define ENABLE_EVENTS" >> ${CONFIGFILE}
|
||||||
echo " * Enabling the Layer3Forwarding Service seems to be the more compatible" >> ${CONFIGFILE}
|
|
||||||
echo " * option. */" >> ${CONFIGFILE}
|
|
||||||
echo "/*#define HAS_DUMMY_SERVICE*/" >> ${CONFIGFILE}
|
|
||||||
echo "#define ENABLE_L3F_SERVICE" >> ${CONFIGFILE}
|
|
||||||
echo "" >> ${CONFIGFILE}
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
echo "/* Experimental UPnP Events support. */" >> ${CONFIGFILE}
|
echo "/* Enable NETGEAR ReadyNAS-specific tweaks. */" >> ${CONFIGFILE}
|
||||||
echo "/*#define ENABLE_EVENTS*/" >> ${CONFIGFILE}
|
echo "/*#define READYNAS*/" >> ${CONFIGFILE}
|
||||||
echo "" >> ${CONFIGFILE}
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
echo "#endif" >> ${CONFIGFILE}
|
echo "#endif" >> ${CONFIGFILE}
|
||||||
|
40
getifaddr.c
40
getifaddr.c
@ -5,6 +5,8 @@
|
|||||||
* This software is subject to the conditions detailed
|
* This software is subject to the conditions detailed
|
||||||
* in the LICENCE file provided within the distribution */
|
* in the LICENCE file provided within the distribution */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -53,3 +55,41 @@ getifaddr(const char * ifname, char * buf, int len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
getifhwaddr(const char * ifname, char * buf, int len)
|
||||||
|
{
|
||||||
|
/* SIOCGIFADDR struct ifreq * */
|
||||||
|
int s;
|
||||||
|
struct ifreq ifr;
|
||||||
|
int ifrlen;
|
||||||
|
unsigned char addr[6];
|
||||||
|
char mac_string[4];
|
||||||
|
int i;
|
||||||
|
ifrlen = sizeof(ifr);
|
||||||
|
if( len < 12 )
|
||||||
|
{
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if(s < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "socket(PF_INET, SOCK_DGRAM): %m");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
|
||||||
|
if(ioctl(s, SIOCGIFHWADDR, &ifr, &ifrlen) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "ioctl(s, SIOCGIFHWADDR, ...): %m");
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(s);
|
||||||
|
|
||||||
|
memmove( addr, ifr.ifr_hwaddr.sa_data, 6);
|
||||||
|
for (i=0; i<6; ++i) {
|
||||||
|
sprintf(mac_string, "%2.2x", addr[i]);
|
||||||
|
strcat(buf, mac_string);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -14,5 +14,8 @@
|
|||||||
int
|
int
|
||||||
getifaddr(const char * ifname, char * buf, int len);
|
getifaddr(const char * ifname, char * buf, int len);
|
||||||
|
|
||||||
|
int
|
||||||
|
getifhwaddr(const char * ifname, char * buf, int len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
507
metadata.c
507
metadata.c
@ -15,20 +15,23 @@
|
|||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
#define LIBDLNA_SUPPORT 1
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include <taglib/tag_c.h>
|
#include <taglib/tag_c.h>
|
||||||
#include <libexif/exif-loader.h>
|
#include <libexif/exif-loader.h>
|
||||||
#if LIBDLNA_SUPPORT
|
#include <avutil.h>
|
||||||
#include <dlna.h>
|
#include <avcodec.h>
|
||||||
#endif
|
#include <avformat.h>
|
||||||
|
|
||||||
#include "upnpglobalvars.h"
|
#include "upnpglobalvars.h"
|
||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
@ -36,6 +39,17 @@
|
|||||||
|
|
||||||
#define FLAG_ARTIST 0x01
|
#define FLAG_ARTIST 0x01
|
||||||
|
|
||||||
|
/* Audio profile flags */
|
||||||
|
#define MP3 0x00000001
|
||||||
|
#define AC3 0x00000002
|
||||||
|
#define WMA_BASE 0x00000004
|
||||||
|
#define WMA_FULL 0x00000008
|
||||||
|
#define WMA_PRO 0x00000010
|
||||||
|
#define MP2 0x00000020
|
||||||
|
#define PCM 0x00000040
|
||||||
|
#define AAC 0x00000100
|
||||||
|
#define AAC_MULT5 0x00000200
|
||||||
|
|
||||||
int
|
int
|
||||||
ends_with(const char * haystack, const char * needle)
|
ends_with(const char * haystack, const char * needle)
|
||||||
{
|
{
|
||||||
@ -111,6 +125,40 @@ strip_ext(char * name)
|
|||||||
*rindex(name, '.') = '\0';
|
*rindex(name, '.') = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function shamelessly copied from libdlna */
|
||||||
|
#define MPEG_TS_SYNC_CODE 0x47
|
||||||
|
#define MPEG_TS_PACKET_LENGTH_DLNA 192 /* prepends 4 bytes to TS packet */
|
||||||
|
int
|
||||||
|
dlna_timestamp_is_present(const char * filename)
|
||||||
|
{
|
||||||
|
unsigned char buffer[2*MPEG_TS_PACKET_LENGTH_DLNA+1];
|
||||||
|
int fd, i;
|
||||||
|
|
||||||
|
/* read file header */
|
||||||
|
fd = open(filename, O_RDONLY);
|
||||||
|
read(fd, buffer, MPEG_TS_PACKET_LENGTH_DLNA*2);
|
||||||
|
close(fd);
|
||||||
|
for( i=0; i < MPEG_TS_PACKET_LENGTH_DLNA; i++ )
|
||||||
|
{
|
||||||
|
if( buffer[i] == MPEG_TS_SYNC_CODE )
|
||||||
|
{
|
||||||
|
if (buffer[i + MPEG_TS_PACKET_LENGTH_DLNA] == MPEG_TS_SYNC_CODE)
|
||||||
|
{
|
||||||
|
if (buffer[i] == 0x00 && buffer [i+1] == 0x00 &&
|
||||||
|
buffer [i+2] == 0x00 && buffer [i+3] == 0x00)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
sqlite_int64
|
sqlite_int64
|
||||||
GetFolderMetadata(const char * name, const char * artist)
|
GetFolderMetadata(const char * name, const char * artist)
|
||||||
{
|
{
|
||||||
@ -239,23 +287,33 @@ GetAudioMetadata(const char * path, char * name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if( 1 ) // Switch on audio file type
|
/* Switch on audio file type */
|
||||||
|
if( ends_with(path, ".mp3") )
|
||||||
{
|
{
|
||||||
strcpy(dlna_pn, "MP3;DLNA.ORG_OP=01");
|
strcpy(dlna_pn, "MP3;DLNA.ORG_OP=01");
|
||||||
strcpy(mime, "audio/mpeg");
|
strcpy(mime, "audio/mpeg");
|
||||||
}
|
}
|
||||||
|
else if( ends_with(path, ".flac") || ends_with(path, ".fla") || ends_with(path, ".flc") )
|
||||||
|
{
|
||||||
|
strcpy(mime, "audio/x-flac");
|
||||||
|
}
|
||||||
|
else if( ends_with(path, ".m4a") || ends_with(path, ".aac") )
|
||||||
|
{
|
||||||
|
//strcpy(dlna_pn, "MP3;DLNA.ORG_OP=01");
|
||||||
|
strcpy(mime, "audio/mp4");
|
||||||
|
}
|
||||||
|
|
||||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||||
" (SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, DATE,"
|
" (PATH, SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, DATE,"
|
||||||
" TITLE, ARTIST, ALBUM, GENRE, COMMENT, TRACK, DLNA_PN, MIME) "
|
" TITLE, CREATOR, ARTIST, ALBUM, GENRE, COMMENT, TRACK, DLNA_PN, MIME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" (%llu, '%s', %d, %d, %d, %Q, %Q, %Q, %Q, %Q, %Q, %d, '%s', '%s');",
|
" (%Q, %llu, '%s', %d, %d, %d, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %d, '%s', '%s');",
|
||||||
size, duration, taglib_audioproperties_channels(properties),
|
path, size, duration, taglib_audioproperties_channels(properties),
|
||||||
taglib_audioproperties_bitrate(properties)*1024,
|
taglib_audioproperties_bitrate(properties)*1024,
|
||||||
taglib_audioproperties_samplerate(properties),
|
taglib_audioproperties_samplerate(properties),
|
||||||
strlen(date) ? date : NULL,
|
strlen(date) ? date : NULL,
|
||||||
title,
|
title,
|
||||||
artist,
|
artist, artist,
|
||||||
album,
|
album,
|
||||||
genre,
|
genre,
|
||||||
comment,
|
comment,
|
||||||
@ -388,10 +446,10 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
asprintf(&m.resolution, "%dx%d", width, height);
|
asprintf(&m.resolution, "%dx%d", width, height);
|
||||||
|
|
||||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||||
" (TITLE, SIZE, DATE, RESOLUTION, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
|
" (PATH, TITLE, SIZE, DATE, RESOLUTION, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('%q', %llu, '%s', %Q, %d, '%q', %Q, %Q);",
|
" (%Q, '%q', %llu, '%s', %Q, %d, '%q', %Q, %Q);",
|
||||||
name, size, date, m.resolution, thumb, model, m.dlna_pn, m.mime);
|
path, name, size, date, m.resolution, thumb, model, m.dlna_pn, m.mime);
|
||||||
//DEBUG printf("SQL: %s\n", sql);
|
//DEBUG printf("SQL: %s\n", sql);
|
||||||
if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
|
if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
|
||||||
{
|
{
|
||||||
@ -414,6 +472,36 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AAC_INVALID = 0,
|
||||||
|
AAC_MAIN = 1, /* AAC Main */
|
||||||
|
AAC_LC = 2, /* AAC Low complexity */
|
||||||
|
AAC_SSR = 3, /* AAC SSR */
|
||||||
|
AAC_LTP = 4, /* AAC Long term prediction */
|
||||||
|
AAC_HE = 5, /* AAC High efficiency (SBR) */
|
||||||
|
AAC_SCALE = 6, /* Scalable */
|
||||||
|
AAC_TWINVQ = 7, /* TwinVQ */
|
||||||
|
AAC_CELP = 8, /* CELP */
|
||||||
|
AAC_HVXC = 9, /* HVXC */
|
||||||
|
AAC_TTSI = 12, /* TTSI */
|
||||||
|
AAC_MS = 13, /* Main synthetic */
|
||||||
|
AAC_WAVE = 14, /* Wavetable synthesis */
|
||||||
|
AAC_MIDI = 15, /* General MIDI */
|
||||||
|
AAC_FX = 16, /* Algorithmic Synthesis and Audio FX */
|
||||||
|
AAC_LC_ER = 17, /* AAC Low complexity with error recovery */
|
||||||
|
AAC_LTP_ER = 19, /* AAC Long term prediction with error recovery */
|
||||||
|
AAC_SCALE_ER = 20, /* AAC scalable with error recovery */
|
||||||
|
AAC_TWINVQ_ER = 21, /* TwinVQ with error recovery */
|
||||||
|
AAC_BSAC_ER = 22, /* BSAC with error recovery */
|
||||||
|
AAC_LD_ER = 23, /* AAC LD with error recovery */
|
||||||
|
AAC_CELP_ER = 24, /* CELP with error recovery */
|
||||||
|
AAC_HXVC_ER = 25, /* HXVC with error recovery */
|
||||||
|
AAC_HILN_ER = 26, /* HILN with error recovery */
|
||||||
|
AAC_PARAM_ER = 27, /* Parametric with error recovery */
|
||||||
|
AAC_SSC = 28, /* AAC SSC */
|
||||||
|
AAC_HE_L3 = 31, /* Reserved : seems to be HeAAC L3 */
|
||||||
|
} aac_object_type_t;
|
||||||
|
|
||||||
sqlite_int64
|
sqlite_int64
|
||||||
GetVideoMetadata(const char * path, char * name)
|
GetVideoMetadata(const char * path, char * name)
|
||||||
{
|
{
|
||||||
@ -421,78 +509,373 @@ GetVideoMetadata(const char * path, char * name)
|
|||||||
struct stat file;
|
struct stat file;
|
||||||
char *sql;
|
char *sql;
|
||||||
char *zErrMsg = NULL;
|
char *zErrMsg = NULL;
|
||||||
int ret;
|
int ret, i;
|
||||||
|
struct tm *modtime;
|
||||||
|
char date[20];
|
||||||
|
AVFormatContext *ctx;
|
||||||
|
int audio_stream = -1, video_stream = -1;
|
||||||
|
int audio_profile = 0;
|
||||||
|
ts_timestamp_t ts_timestamp = NONE;
|
||||||
|
int duration, hours, min, sec, ms;
|
||||||
|
aac_object_type_t aac_type = 0;
|
||||||
metadata_t m;
|
metadata_t m;
|
||||||
memset(&m, '\0', sizeof(m));
|
memset(&m, '\0', sizeof(m));
|
||||||
|
date[0] = '\0';
|
||||||
|
|
||||||
//DEBUG printf("Parsing %s...\n", path);
|
//DEBUG printf("Parsing %s...\n", path);
|
||||||
if ( stat(path, &file) == 0 )
|
if ( stat(path, &file) == 0 )
|
||||||
|
{
|
||||||
|
modtime = localtime(&file.st_mtime);
|
||||||
|
strftime(date, sizeof(date), "%FT%T", modtime);
|
||||||
size = file.st_size;
|
size = file.st_size;
|
||||||
|
}
|
||||||
strip_ext(name);
|
strip_ext(name);
|
||||||
//DEBUG printf(" * size: %d\n", size);
|
//DEBUG printf(" * size: %d\n", size);
|
||||||
|
|
||||||
#if LIBDLNA_SUPPORT
|
av_register_all();
|
||||||
dlna_t *dlna;
|
if( av_open_input_file(&ctx, path, NULL, 0, NULL) == 0 )
|
||||||
dlna_profile_t *p;
|
|
||||||
dlna_item_t *item;
|
|
||||||
|
|
||||||
dlna = dlna_init();
|
|
||||||
dlna_register_all_media_profiles(dlna);
|
|
||||||
|
|
||||||
item = dlna_item_new(dlna, path);
|
|
||||||
if (item)
|
|
||||||
{
|
{
|
||||||
if (item->properties)
|
av_find_stream_info(ctx);
|
||||||
|
//dump_format(ctx, 0, NULL, 0);
|
||||||
|
for( i=0; i<ctx->nb_streams; i++)
|
||||||
{
|
{
|
||||||
if( strlen(item->properties->duration) )
|
if( audio_stream == -1 &&
|
||||||
m.duration = strdup(item->properties->duration);
|
ctx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO )
|
||||||
if( item->properties->bitrate )
|
{
|
||||||
asprintf(&m.bitrate, "%d", item->properties->bitrate);
|
audio_stream = i;
|
||||||
if( item->properties->sample_frequency )
|
continue;
|
||||||
asprintf(&m.frequency, "%d", item->properties->sample_frequency);
|
}
|
||||||
if( item->properties->bps )
|
else if( video_stream == -1 &&
|
||||||
asprintf(&m.bps, "%d", item->properties->bps);
|
ctx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
||||||
if( item->properties->channels )
|
{
|
||||||
asprintf(&m.channels, "%d", item->properties->channels);
|
video_stream = i;
|
||||||
m.resolution = item->properties->resolution;
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if( audio_stream >= 0 )
|
||||||
|
{
|
||||||
p = dlna_guess_media_profile(dlna, path);
|
switch( ctx->streams[audio_stream]->codec->codec_id )
|
||||||
if (p)
|
{
|
||||||
{
|
case CODEC_ID_MP3:
|
||||||
m.mime = strdup(p->mime);
|
audio_profile = MP3;
|
||||||
asprintf(&m.dlna_pn, "%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", p->id);
|
break;
|
||||||
|
case CODEC_ID_AAC:
|
||||||
|
if( !ctx->streams[audio_stream]->codec->extradata_size ||
|
||||||
|
!ctx->streams[audio_stream]->codec->extradata )
|
||||||
|
printf("No AAC type\n");
|
||||||
|
else
|
||||||
|
aac_type = ctx->streams[audio_stream]->codec->extradata[0] >> 3;
|
||||||
|
switch( aac_type )
|
||||||
|
{
|
||||||
|
/* AAC Low Complexity variants */
|
||||||
|
case AAC_LC:
|
||||||
|
case AAC_LC_ER:
|
||||||
|
if( ctx->streams[audio_stream]->codec->sample_rate < 8000 ||
|
||||||
|
ctx->streams[audio_stream]->codec->sample_rate > 48000 )
|
||||||
|
{
|
||||||
|
printf("Unsupported AAC: sample rate is not 8000 < %d < 48000\n",
|
||||||
|
ctx->streams[audio_stream]->codec->sample_rate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* AAC @ Level 1/2 */
|
||||||
|
if( ctx->streams[audio_stream]->codec->channels <= 2 &&
|
||||||
|
ctx->streams[audio_stream]->codec->bit_rate <= 576000 )
|
||||||
|
audio_profile = AAC;
|
||||||
|
else if( ctx->streams[audio_stream]->codec->channels <= 6 &&
|
||||||
|
ctx->streams[audio_stream]->codec->bit_rate <= 1440000 )
|
||||||
|
audio_profile = AAC_MULT5;
|
||||||
|
else
|
||||||
|
printf("Unhandled AAC: %d channels, %d bitrate\n",
|
||||||
|
ctx->streams[audio_stream]->codec->channels,
|
||||||
|
ctx->streams[audio_stream]->codec->bit_rate);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("Unhandled AAC type [%d]\n", aac_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CODEC_ID_AC3:
|
||||||
|
case CODEC_ID_DTS:
|
||||||
|
audio_profile = AC3;
|
||||||
|
break;
|
||||||
|
case CODEC_ID_WMAV1:
|
||||||
|
case CODEC_ID_WMAV2:
|
||||||
|
/* WMA Baseline: stereo, up to 48 KHz, up to 192,999 bps */
|
||||||
|
if ( ctx->streams[audio_stream]->codec->bit_rate <= 193000 )
|
||||||
|
audio_profile = WMA_BASE;
|
||||||
|
/* WMA Full: stereo, up to 48 KHz, up to 385 Kbps */
|
||||||
|
else if ( ctx->streams[audio_stream]->codec->bit_rate <= 385000 )
|
||||||
|
audio_profile = WMA_FULL;
|
||||||
|
break;
|
||||||
|
case CODEC_ID_WMAPRO:
|
||||||
|
audio_profile = WMA_PRO;
|
||||||
|
break;
|
||||||
|
case CODEC_ID_MP2:
|
||||||
|
audio_profile = MP2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if( (ctx->streams[audio_stream]->codec->codec_id >= CODEC_ID_PCM_S16LE) &&
|
||||||
|
(ctx->streams[audio_stream]->codec->codec_id <= CODEC_ID_PCM_F64LE) )
|
||||||
|
audio_profile = PCM;
|
||||||
|
else
|
||||||
|
printf("Unhandled audio codec [%X]\n", ctx->streams[audio_stream]->codec->codec_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
asprintf(&m.frequency, "%u", ctx->streams[audio_stream]->codec->sample_rate);
|
||||||
|
#if LIBAVCODEC_VERSION_MAJOR >= 52
|
||||||
|
asprintf(&m.bps, "%u", ctx->streams[audio_stream]->codec->bits_per_coded_sample);
|
||||||
|
#else
|
||||||
|
asprintf(&m.bps, "%u", ctx->streams[audio_stream]->codec->bits_per_sample);
|
||||||
|
#endif
|
||||||
|
asprintf(&m.channels, "%u", ctx->streams[audio_stream]->codec->channels);
|
||||||
|
}
|
||||||
|
if( video_stream >= 0 )
|
||||||
|
{
|
||||||
|
//DEBUG printf("Container: '%s' [%s]\n", ctx->iformat->name, path);
|
||||||
|
asprintf(&m.resolution, "%dx%d", ctx->streams[video_stream]->codec->width, ctx->streams[video_stream]->codec->height);
|
||||||
|
asprintf(&m.bitrate, "%u", ctx->bit_rate / 8);
|
||||||
|
if( ctx->duration > 0 ) {
|
||||||
|
duration = (int)(ctx->duration / AV_TIME_BASE);
|
||||||
|
hours = (int)(duration / 3600);
|
||||||
|
min = (int)(duration / 60 % 60);
|
||||||
|
sec = (int)(duration % 60);
|
||||||
|
ms = (int)(ctx->duration / (AV_TIME_BASE/1000) % 1000);
|
||||||
|
asprintf(&m.duration, "%d:%02d:%02d.%03d", hours, min, sec, ms);
|
||||||
|
}
|
||||||
|
/* NOTE: The DLNA spec only provides for ASF (WMV), TS, PS, and MP4 containers -- not AVI. */
|
||||||
|
switch( ctx->streams[video_stream]->codec->codec_id )
|
||||||
|
{
|
||||||
|
case CODEC_ID_MPEG1VIDEO:
|
||||||
|
if( strcmp(ctx->iformat->name, "mpeg") == 0 )
|
||||||
|
{
|
||||||
|
asprintf(&m.dlna_pn, "MPEG1;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||||
|
asprintf(&m.mime, "video/mpeg");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CODEC_ID_MPEG2VIDEO:
|
||||||
|
if( strcmp(ctx->iformat->name, "mpegts") == 0 )
|
||||||
|
{
|
||||||
|
printf("Stream %d of %s is %s MPEG2 TS\n", video_stream, path, m.resolution);
|
||||||
|
char res;
|
||||||
|
tsinfo_t * ts = ctx->priv_data;
|
||||||
|
if( ts->packet_size == 192 )
|
||||||
|
{
|
||||||
|
asprintf(&m.dlna_pn, "MPEG_TS_HD_NA;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||||
|
asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
|
||||||
|
}
|
||||||
|
else if( ts->packet_size == 188 )
|
||||||
|
{
|
||||||
|
if( (ctx->streams[video_stream]->codec->width >= 1280) &&
|
||||||
|
(ctx->streams[video_stream]->codec->height >= 720) )
|
||||||
|
res = 'H';
|
||||||
|
else
|
||||||
|
res = 'S';
|
||||||
|
asprintf(&m.dlna_pn, "MPEG_TS_%cD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0", res);
|
||||||
|
asprintf(&m.mime, "video/mpeg");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( strcmp(ctx->iformat->name, "mpeg") == 0 )
|
||||||
|
{
|
||||||
|
printf("Stream %d of %s is %s MPEG2 PS\n", video_stream, path, m.resolution);
|
||||||
|
char region[5];
|
||||||
|
if( (ctx->streams[video_stream]->codec->height == 576) ||
|
||||||
|
(ctx->streams[video_stream]->codec->height == 288) )
|
||||||
|
strcpy(region, "PAL");
|
||||||
|
else
|
||||||
|
strcpy(region, "NTSC");
|
||||||
|
asprintf(&m.dlna_pn, "MPEG_PS_%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", region);
|
||||||
|
asprintf(&m.mime, "video/mpeg");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Stream %d of %s [UNKNOWN CONTAINER] is %s MPEG2\n", video_stream, path, m.resolution);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CODEC_ID_H264:
|
||||||
|
if( strcmp(ctx->iformat->name, "mpegts") == 0 )
|
||||||
|
{
|
||||||
|
tsinfo_t * ts = ctx->priv_data;
|
||||||
|
if( ts->packet_size == 192 )
|
||||||
|
{
|
||||||
|
if( dlna_timestamp_is_present(path) )
|
||||||
|
ts_timestamp = VALID;
|
||||||
|
else
|
||||||
|
ts_timestamp = EMPTY;
|
||||||
|
}
|
||||||
|
char res = '\0';
|
||||||
|
if( ctx->streams[video_stream]->codec->width <= 720 &&
|
||||||
|
ctx->streams[video_stream]->codec->height <= 576 &&
|
||||||
|
ctx->streams[video_stream]->codec->bit_rate <= 10000000 )
|
||||||
|
res = 'S';
|
||||||
|
else if( ctx->streams[video_stream]->codec->width <= 1920 &&
|
||||||
|
ctx->streams[video_stream]->codec->height <= 1152 &&
|
||||||
|
ctx->streams[video_stream]->codec->bit_rate <= 20000000 )
|
||||||
|
res = 'H';
|
||||||
|
if( res )
|
||||||
|
{
|
||||||
|
switch( audio_profile )
|
||||||
|
{
|
||||||
|
case MP3:
|
||||||
|
asprintf(&m.dlna_pn, "AVC_TS_MP_HD_MPEG1_L3%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0",
|
||||||
|
ts_timestamp==NONE?"_ISO" : ts_timestamp==VALID?"_T":"");
|
||||||
|
break;
|
||||||
|
case AC3:
|
||||||
|
asprintf(&m.dlna_pn, "AVC_TS_MP_HD_AC3%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0",
|
||||||
|
ts_timestamp==NONE?"_ISO" : ts_timestamp==VALID?"_T":"");
|
||||||
|
break;
|
||||||
|
case AAC_MULT5:
|
||||||
|
asprintf(&m.dlna_pn, "AVC_TS_MP_HD_AAC_MULT5%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0",
|
||||||
|
ts_timestamp==NONE?"_ISO" : ts_timestamp==VALID?"_T":"");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("No DLNA profile found for TS/AVC/%cD file %s\n", res, path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if( m.dlna_pn && (ts_timestamp != NONE) )
|
||||||
|
asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Unsupported h.264 video profile! [%dx%d, %dbps]\n",
|
||||||
|
ctx->streams[video_stream]->codec->width,
|
||||||
|
ctx->streams[video_stream]->codec->height,
|
||||||
|
ctx->streams[video_stream]->codec->bit_rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
|
||||||
|
{
|
||||||
|
/* AVC wrapped in MP4 only has SD profiles - 10 Mbps max */
|
||||||
|
if( ctx->streams[video_stream]->codec->width <= 720 &&
|
||||||
|
ctx->streams[video_stream]->codec->height <= 576 &&
|
||||||
|
ctx->streams[video_stream]->codec->bit_rate <= 10000000 )
|
||||||
|
{
|
||||||
|
switch( audio_profile )
|
||||||
|
{
|
||||||
|
case AC3:
|
||||||
|
asprintf(&m.dlna_pn, "AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||||
|
break;
|
||||||
|
case AAC_MULT5:
|
||||||
|
asprintf(&m.dlna_pn, "AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("No DLNA profile found for MP4/AVC/SD file %s\n", path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Stream %d of %s is h.264\n", video_stream, path);
|
||||||
|
break;
|
||||||
|
case CODEC_ID_MPEG4:
|
||||||
|
if( ctx->streams[video_stream]->codec->codec_tag == ff_get_fourcc("XVID") )
|
||||||
|
{
|
||||||
|
printf("Stream %d of %s is %s XViD\n", video_stream, path, m.resolution);
|
||||||
|
asprintf(&m.mime, "video/divx");
|
||||||
|
}
|
||||||
|
else if( ctx->streams[video_stream]->codec->codec_tag == ff_get_fourcc("DX50") )
|
||||||
|
{
|
||||||
|
printf("Stream %d of %s is %s DiVX5\n", video_stream, path, m.resolution);
|
||||||
|
asprintf(&m.mime, "video/divx");
|
||||||
|
}
|
||||||
|
else if( ctx->streams[video_stream]->codec->codec_tag == ff_get_fourcc("DIVX") )
|
||||||
|
{
|
||||||
|
printf("Stream %d of %s is DiVX\n", video_stream, path);
|
||||||
|
asprintf(&m.mime, "video/divx");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Stream %d of %s is MPEG4 [%X]\n", video_stream, path, ctx->streams[video_stream]->codec->codec_tag);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CODEC_ID_WMV3:
|
||||||
|
case CODEC_ID_VC1:
|
||||||
|
printf("Stream %d of %s is VC1\n", video_stream, path);
|
||||||
|
char profile[5]; profile[0] = '\0';
|
||||||
|
asprintf(&m.mime, "video/x-ms-wmv");
|
||||||
|
if( (ctx->streams[video_stream]->codec->width <= 352) &&
|
||||||
|
(ctx->streams[video_stream]->codec->height <= 288) &&
|
||||||
|
(ctx->bit_rate/8 <= 384000) )
|
||||||
|
{
|
||||||
|
if( audio_profile == MP3 )
|
||||||
|
strcpy(profile, "MP3");
|
||||||
|
else if( audio_profile == WMA_BASE )
|
||||||
|
strcpy(profile, "BASE");
|
||||||
|
asprintf(&m.dlna_pn, "WMVSPML_%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", profile);
|
||||||
|
}
|
||||||
|
else if( (ctx->streams[video_stream]->codec->width <= 720) &&
|
||||||
|
(ctx->streams[video_stream]->codec->height <= 576) &&
|
||||||
|
(ctx->bit_rate/8 <= 10000000) )
|
||||||
|
{
|
||||||
|
if( audio_profile == WMA_PRO )
|
||||||
|
strcpy(profile, "PRO");
|
||||||
|
else if( audio_profile == WMA_FULL )
|
||||||
|
strcpy(profile, "FULL");
|
||||||
|
else if( audio_profile == WMA_BASE )
|
||||||
|
strcpy(profile, "BASE");
|
||||||
|
asprintf(&m.dlna_pn, "WMVMED_%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", profile);
|
||||||
|
}
|
||||||
|
else if( (ctx->streams[video_stream]->codec->width <= 1920) &&
|
||||||
|
(ctx->streams[video_stream]->codec->height <= 1080) &&
|
||||||
|
(ctx->bit_rate/8 <= 20000000) )
|
||||||
|
{
|
||||||
|
if( audio_profile == WMA_FULL )
|
||||||
|
strcpy(profile, "FULL");
|
||||||
|
else if( audio_profile == WMA_PRO )
|
||||||
|
strcpy(profile, "PRO");
|
||||||
|
asprintf(&m.dlna_pn, "WMVHIGH_%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", profile);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CODEC_ID_XVID:
|
||||||
|
printf("Stream %d of %s is %s UNKNOWN XVID\n", video_stream, path, m.resolution);
|
||||||
|
break;
|
||||||
|
case CODEC_ID_MSMPEG4V1:
|
||||||
|
printf("Stream %d of %s is %s MS MPEG4 v1\n", video_stream, path, m.resolution);
|
||||||
|
case CODEC_ID_MSMPEG4V3:
|
||||||
|
printf("Stream %d of %s is %s MS MPEG4 v3\n", video_stream, path, m.resolution);
|
||||||
|
asprintf(&m.mime, "video/avi");
|
||||||
|
break;
|
||||||
|
case CODEC_ID_H263I:
|
||||||
|
printf("Stream %d of %s is h.263i\n", video_stream, path);
|
||||||
|
break;
|
||||||
|
case CODEC_ID_MJPEG:
|
||||||
|
printf("Stream %d of %s is MJPEG\n", video_stream, path);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("Stream %d of %s is %d\n", video_stream, path, ctx->streams[video_stream]->codec->codec_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( !m.mime )
|
||||||
|
{
|
||||||
|
if( strcmp(ctx->iformat->name, "avi") == 0 )
|
||||||
|
asprintf(&m.mime, "video/x-msvideo");
|
||||||
|
else if( strcmp(ctx->iformat->name, "mpegts") == 0 )
|
||||||
|
asprintf(&m.mime, "video/mpeg");
|
||||||
|
else if( strcmp(ctx->iformat->name, "mpeg") == 0 )
|
||||||
|
asprintf(&m.mime, "video/mpeg");
|
||||||
|
else if( strcmp(ctx->iformat->name, "asf") == 0 )
|
||||||
|
asprintf(&m.mime, "video/x-ms-wmv");
|
||||||
|
else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
|
||||||
|
asprintf(&m.mime, "video/mp4");
|
||||||
|
}
|
||||||
|
av_close_input_file(ctx);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf ("Unknown format [%s]\n", path);
|
printf("Opening %s failed!\n", path);
|
||||||
if( ends_with(path, ".mpg") || ends_with(path, ".mpeg") || ends_with(path, ".ts") )
|
|
||||||
asprintf(&m.mime, "video/mpeg");
|
|
||||||
else if( ends_with(path, ".avi") || ends_with(path, ".divx") )
|
|
||||||
asprintf(&m.mime, "video/divx");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||||
" (SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, RESOLUTION,"
|
" (PATH, SIZE, DURATION, DATE, CHANNELS, BITRATE, SAMPLERATE, RESOLUTION,"
|
||||||
" TITLE, DLNA_PN, MIME) "
|
" TITLE, DLNA_PN, MIME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" (%lld, %Q, %Q, %Q, %Q, %Q, '%q', %Q, '%q');",
|
" (%Q, %lld, %Q, %Q, %Q, %Q, %Q, %Q, '%q', %Q, '%q');",
|
||||||
size, m.duration,
|
path, size, m.duration,
|
||||||
|
strlen(date) ? date : NULL,
|
||||||
m.channels,
|
m.channels,
|
||||||
m.bitrate,
|
m.bitrate,
|
||||||
m.frequency,
|
m.frequency,
|
||||||
m.resolution,
|
m.resolution,
|
||||||
name, m.dlna_pn, m.mime);
|
name, m.dlna_pn, m.mime);
|
||||||
dlna_item_free(item);
|
|
||||||
dlna_uninit(dlna);
|
|
||||||
#else // LIBDLNA_SUPPORT
|
|
||||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
|
||||||
" (TITLE, SIZE, MIME) "
|
|
||||||
"VALUES"
|
|
||||||
" ('%q', %d, %Q);",
|
|
||||||
name, size, "video/mpeg");*/
|
|
||||||
#endif // LIBDLNA_SUPPORT
|
|
||||||
//DEBUG printf("SQL: %s\n", sql);
|
//DEBUG printf("SQL: %s\n", sql);
|
||||||
if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
|
if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
|
||||||
{
|
{
|
||||||
@ -512,6 +895,8 @@ GetVideoMetadata(const char * path, char * name)
|
|||||||
free(m.mime);
|
free(m.mime);
|
||||||
if( m.duration )
|
if( m.duration )
|
||||||
free(m.duration);
|
free(m.duration);
|
||||||
|
if( m.resolution )
|
||||||
|
free(m.resolution);
|
||||||
if( m.bitrate )
|
if( m.bitrate )
|
||||||
free(m.bitrate);
|
free(m.bitrate);
|
||||||
if( m.frequency )
|
if( m.frequency )
|
||||||
|
11
metadata.h
11
metadata.h
@ -26,6 +26,17 @@ typedef struct metadata_s {
|
|||||||
char *dlna_pn;
|
char *dlna_pn;
|
||||||
} metadata_t;
|
} metadata_t;
|
||||||
|
|
||||||
|
typedef struct tsinfo_s {
|
||||||
|
int x;
|
||||||
|
int packet_size;
|
||||||
|
} tsinfo_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NONE,
|
||||||
|
EMPTY,
|
||||||
|
VALID
|
||||||
|
} ts_timestamp_t;
|
||||||
|
|
||||||
int
|
int
|
||||||
ends_with(const char * haystack, const char * needle);
|
ends_with(const char * haystack, const char * needle);
|
||||||
|
|
||||||
|
138
minidlna.c
138
minidlna.c
@ -208,6 +208,29 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
getfriendlyname(char * buf, int len)
|
||||||
|
{
|
||||||
|
char * hn = calloc(1, 256);
|
||||||
|
if( gethostname(hn, 256) == 0 )
|
||||||
|
{
|
||||||
|
strncpy(buf, hn, len-1);
|
||||||
|
buf[len] = '\0';
|
||||||
|
*strstr(buf, ".") = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strcpy(buf, "Unknown");
|
||||||
|
}
|
||||||
|
free(hn);
|
||||||
|
strcat(buf, ": ");
|
||||||
|
#ifdef READYNAS
|
||||||
|
strncat(buf, "ReadyNAS", len-strlen(buf)-1);
|
||||||
|
#else
|
||||||
|
strncat(buf, getenv("LOGNAME"), len-strlen(buf)-1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* init phase :
|
/* init phase :
|
||||||
* 1) read configuration file
|
* 1) read configuration file
|
||||||
* 2) read command line arguments
|
* 2) read command line arguments
|
||||||
@ -229,6 +252,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
/*const char * logfilename = 0;*/
|
/*const char * logfilename = 0;*/
|
||||||
const char * presurl = 0;
|
const char * presurl = 0;
|
||||||
const char * optionsfile = "/etc/minidlna.conf";
|
const char * optionsfile = "/etc/minidlna.conf";
|
||||||
|
char * mac_str = calloc(1, 64);
|
||||||
|
|
||||||
/* first check if "-f" option is used */
|
/* first check if "-f" option is used */
|
||||||
for(i=2; i<argc; i++)
|
for(i=2; i<argc; i++)
|
||||||
@ -241,6 +265,19 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* set up uuid based on mac address */
|
||||||
|
if( (getifhwaddr("eth0", mac_str, 64) < 0) &&
|
||||||
|
(getifhwaddr("eth1", mac_str, 64) < 0) )
|
||||||
|
{
|
||||||
|
printf("No MAC addresses found!\n");
|
||||||
|
strcpy(mac_str, "554e4b4e4f57");
|
||||||
|
}
|
||||||
|
strcpy(uuidvalue+5, "4d696e69-444c-164e-9d41-");
|
||||||
|
strncat(uuidvalue, mac_str, 12);
|
||||||
|
free(mac_str);
|
||||||
|
|
||||||
|
getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN);
|
||||||
|
|
||||||
/*v->n_lan_addr = 0;*/
|
/*v->n_lan_addr = 0;*/
|
||||||
char ext_ip_addr[INET_ADDRSTRLEN];
|
char ext_ip_addr[INET_ADDRSTRLEN];
|
||||||
if( (getifaddr("eth0", ext_ip_addr, INET_ADDRSTRLEN) < 0) &&
|
if( (getifaddr("eth0", ext_ip_addr, INET_ADDRSTRLEN) < 0) &&
|
||||||
@ -270,12 +307,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
{
|
{
|
||||||
switch(ary_options[i].id)
|
switch(ary_options[i].id)
|
||||||
{
|
{
|
||||||
case UPNPEXT_IFNAME:
|
|
||||||
ext_if_name = ary_options[i].value;
|
|
||||||
break;
|
|
||||||
case UPNPEXT_IP:
|
|
||||||
use_ext_ip_addr = ary_options[i].value;
|
|
||||||
break;
|
|
||||||
case UPNPLISTENING_IP:
|
case UPNPLISTENING_IP:
|
||||||
if(n_lan_addr < MAX_LAN_ADDR)/* if(v->n_lan_addr < MAX_LAN_ADDR)*/
|
if(n_lan_addr < MAX_LAN_ADDR)/* if(v->n_lan_addr < MAX_LAN_ADDR)*/
|
||||||
{
|
{
|
||||||
@ -313,10 +344,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
if(strcmp(ary_options[i].value, "yes") == 0)
|
if(strcmp(ary_options[i].value, "yes") == 0)
|
||||||
SETFLAG(LOGPACKETSMASK); /*logpackets = 1;*/
|
SETFLAG(LOGPACKETSMASK); /*logpackets = 1;*/
|
||||||
break;
|
break;
|
||||||
case UPNPUUID:
|
|
||||||
strncpy(uuidvalue+5, ary_options[i].value,
|
|
||||||
strlen(uuidvalue+5) + 1);
|
|
||||||
break;
|
|
||||||
case UPNPSERIAL:
|
case UPNPSERIAL:
|
||||||
strncpy(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN);
|
strncpy(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN);
|
||||||
serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0';
|
serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0';
|
||||||
@ -331,20 +358,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
case UPNPCLEANINTERVAL:
|
case UPNPCLEANINTERVAL:
|
||||||
v->clean_ruleset_interval = atoi(ary_options[i].value);
|
v->clean_ruleset_interval = atoi(ary_options[i].value);
|
||||||
break;
|
break;
|
||||||
#ifdef USE_PF
|
|
||||||
case UPNPQUEUE:
|
|
||||||
queue = ary_options[i].value;
|
|
||||||
break;
|
|
||||||
case UPNPTAG:
|
|
||||||
tag = ary_options[i].value;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
#ifdef PF_ENABLE_FILTER_RULES
|
|
||||||
case UPNPQUICKRULES:
|
|
||||||
if(strcmp(ary_options[i].value, "no") == 0)
|
|
||||||
SETFLAG(PFNOQUICKRULESMASK);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case UPNPSECUREMODE:
|
case UPNPSECUREMODE:
|
||||||
if(strcmp(ary_options[i].value, "yes") == 0)
|
if(strcmp(ary_options[i].value, "yes") == 0)
|
||||||
SETFLAG(SECUREMODEMASK);
|
SETFLAG(SECUREMODEMASK);
|
||||||
@ -355,6 +368,10 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
remove(lease_file);
|
remove(lease_file);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case UPNPFRIENDLYNAME:
|
||||||
|
strncpy(friendly_name, ary_options[i].value, FRIENDLYNAME_MAX_LEN);
|
||||||
|
friendly_name[FRIENDLYNAME_MAX_LEN-1] = '\0';
|
||||||
|
break;
|
||||||
case UPNPMEDIADIR:
|
case UPNPMEDIADIR:
|
||||||
strncpy(media_dir, ary_options[i].value, MEDIADIR_MAX_LEN);
|
strncpy(media_dir, ary_options[i].value, MEDIADIR_MAX_LEN);
|
||||||
media_dir[MEDIADIR_MAX_LEN-1] = '\0';
|
media_dir[MEDIADIR_MAX_LEN-1] = '\0';
|
||||||
@ -375,24 +392,12 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
}
|
}
|
||||||
else switch(argv[i][1])
|
else switch(argv[i][1])
|
||||||
{
|
{
|
||||||
case 'o':
|
|
||||||
if(i+1 < argc)
|
|
||||||
use_ext_ip_addr = argv[++i];
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
|
||||||
break;
|
|
||||||
case 't':
|
case 't':
|
||||||
if(i+1 < argc)
|
if(i+1 < argc)
|
||||||
v->notify_interval = atoi(argv[++i]);
|
v->notify_interval = atoi(argv[++i]);
|
||||||
else
|
else
|
||||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||||
break;
|
break;
|
||||||
case 'u':
|
|
||||||
if(i+1 < argc)
|
|
||||||
strncpy(uuidvalue+5, argv[++i], strlen(uuidvalue+5) + 1);
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
|
||||||
break;
|
|
||||||
case 's':
|
case 's':
|
||||||
if(i+1 < argc)
|
if(i+1 < argc)
|
||||||
strncpy(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN);
|
strncpy(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN);
|
||||||
@ -421,26 +426,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
case 'S':
|
case 'S':
|
||||||
SETFLAG(SECUREMODEMASK);
|
SETFLAG(SECUREMODEMASK);
|
||||||
break;
|
break;
|
||||||
case 'i':
|
|
||||||
if(i+1 < argc)
|
|
||||||
ext_if_name = argv[++i];
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
|
||||||
break;
|
|
||||||
#ifdef USE_PF
|
|
||||||
case 'q':
|
|
||||||
if(i+1 < argc)
|
|
||||||
queue = argv[++i];
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
|
||||||
break;
|
|
||||||
case 'T':
|
|
||||||
if(i+1 < argc)
|
|
||||||
tag = argv[++i];
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case 'p':
|
case 'p':
|
||||||
if(i+1 < argc)
|
if(i+1 < argc)
|
||||||
v->port = atoi(argv[++i]);
|
v->port = atoi(argv[++i]);
|
||||||
@ -510,19 +495,15 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!ext_if_name || (/*v->*/n_lan_addr==0) || v->port<=0)
|
if( (/*v->*/n_lan_addr==0) || (v->port<=0) )
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage:\n\t"
|
fprintf(stderr, "Usage:\n\t"
|
||||||
"%s [-f config_file] [-i ext_ifname] [-o ext_ip]\n"
|
"%s [-f config_file] [-i ext_ifname] [-o ext_ip]\n"
|
||||||
"\t\t[-a listening_ip] [-p port] [-d] [-L] [-U] [-S]\n"
|
"\t\t[-a listening_ip] [-p port] [-d] [-L] [-U] [-S]\n"
|
||||||
/*"[-l logfile] " not functionnal */
|
/*"[-l logfile] " not functionnal */
|
||||||
"\t\t[-u uuid] [-s serial] [-m model_number] \n"
|
"\t\t[-s serial] [-m model_number] \n"
|
||||||
"\t\t[-t notify_interval] [-P pid_filename]\n"
|
"\t\t[-t notify_interval] [-P pid_filename]\n"
|
||||||
#ifdef USE_PF
|
|
||||||
"\t\t[-B down up] [-w url] [-q queue] [-T tag]\n"
|
|
||||||
#else
|
|
||||||
"\t\t[-B down up] [-w url]\n"
|
"\t\t[-B down up] [-w url]\n"
|
||||||
#endif
|
|
||||||
"\nNotes:\n\tThere can be one or several listening_ips.\n"
|
"\nNotes:\n\tThere can be one or several listening_ips.\n"
|
||||||
"\tNotify interval is in seconds. Default is 30 seconds.\n"
|
"\tNotify interval is in seconds. Default is 30 seconds.\n"
|
||||||
"\tDefault pid file is %s.\n"
|
"\tDefault pid file is %s.\n"
|
||||||
@ -533,10 +514,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
"of daemon uptime.\n"
|
"of daemon uptime.\n"
|
||||||
"\t-B sets bitrates reported by daemon in bits per second.\n"
|
"\t-B sets bitrates reported by daemon in bits per second.\n"
|
||||||
"\t-w sets the presentation url. Default is http address on port 80\n"
|
"\t-w sets the presentation url. Default is http address on port 80\n"
|
||||||
#ifdef USE_PF
|
|
||||||
"\t-q sets the ALTQ queue in pf.\n"
|
|
||||||
"\t-T sets the tag name in pf.\n"
|
|
||||||
#endif
|
|
||||||
"", argv[0], pidfilename);
|
"", argv[0], pidfilename);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -563,7 +540,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
openlog_option |= LOG_PERROR; /* also log on stderr */
|
openlog_option |= LOG_PERROR; /* also log on stderr */
|
||||||
}
|
}
|
||||||
|
|
||||||
openlog("miniupnpd", openlog_option, LOG_MINIUPNPD);
|
openlog("minidlna", openlog_option, LOG_MINIDLNA);
|
||||||
|
|
||||||
if(!debug_flag)
|
if(!debug_flag)
|
||||||
{
|
{
|
||||||
@ -573,7 +550,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
|
|
||||||
if(checkforrunning(pidfilename) < 0)
|
if(checkforrunning(pidfilename) < 0)
|
||||||
{
|
{
|
||||||
syslog(LOG_ERR, "MiniUPnPd is already running. EXITING");
|
syslog(LOG_ERR, "MiniDLNA is already running. EXITING");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,9 +571,9 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* set signal handler */
|
/* set signal handler */
|
||||||
|
signal(SIGCLD, SIG_IGN);
|
||||||
memset(&sa, 0, sizeof(struct sigaction));
|
memset(&sa, 0, sizeof(struct sigaction));
|
||||||
sa.sa_handler = sigterm;
|
sa.sa_handler = sigterm;
|
||||||
|
|
||||||
if (sigaction(SIGTERM, &sa, NULL))
|
if (sigaction(SIGTERM, &sa, NULL))
|
||||||
{
|
{
|
||||||
syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGTERM");
|
syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGTERM");
|
||||||
@ -641,14 +618,31 @@ main(int argc, char * * argv)
|
|||||||
|
|
||||||
LIST_INIT(&upnphttphead);
|
LIST_INIT(&upnphttphead);
|
||||||
|
|
||||||
if( access("/tmp/files.db", F_OK) )
|
if( access(DB_PATH, F_OK) )
|
||||||
{
|
{
|
||||||
sqlite3_open("/tmp/files.db", &db);
|
sqlite3_open(DB_PATH, &db);
|
||||||
|
freopen("/dev/null", "a", stderr);
|
||||||
ScanDirectory(media_dir, NULL);
|
ScanDirectory(media_dir, NULL);
|
||||||
|
freopen("/proc/self/fd/2", "a", stderr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sqlite3_open("/tmp/files.db", &db);
|
char **result;
|
||||||
|
int rows;
|
||||||
|
sqlite3_open(DB_PATH, &db);
|
||||||
|
if( sqlite3_get_table(db, "pragma user_version", &result, &rows, 0, 0) == SQLITE_OK )
|
||||||
|
{
|
||||||
|
if( atoi(result[1]) != DB_VERSION ) {
|
||||||
|
printf("Database version mismatch; need to recreate...\n");
|
||||||
|
sqlite3_close(db);
|
||||||
|
unlink(DB_PATH);
|
||||||
|
sqlite3_open(DB_PATH, &db);
|
||||||
|
freopen("/dev/null", "a", stderr);
|
||||||
|
ScanDirectory(media_dir, NULL);
|
||||||
|
freopen("/proc/self/fd/2", "a", stderr);
|
||||||
|
}
|
||||||
|
sqlite3_free_table(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
# WAN network interface
|
|
||||||
ext_ifname=eth0
|
|
||||||
|
|
||||||
# port for HTTP (descriptions and SOAP) traffic
|
# port for HTTP (descriptions and SOAP) traffic
|
||||||
port=5555
|
port=5555
|
||||||
|
|
||||||
@ -31,8 +28,6 @@ system_uptime=no
|
|||||||
notify_interval=900
|
notify_interval=900
|
||||||
|
|
||||||
clean_ruleset_interval=600
|
clean_ruleset_interval=600
|
||||||
# uuid : generate your own with "make genuuid"
|
|
||||||
uuid=fc4ec57e-b051-11db-88f8-0060085db3f6
|
|
||||||
|
|
||||||
# serial and model number the daemon will report to clients
|
# serial and model number the daemon will report to clients
|
||||||
# in its XML description
|
# in its XML description
|
||||||
|
@ -133,15 +133,15 @@ OpenAndConfSSDPNotifySocket(in_addr_t addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
memset(&sockname, 0, sizeof(struct sockaddr_in));
|
memset(&sockname, 0, sizeof(struct sockaddr_in));
|
||||||
sockname.sin_family = AF_INET;
|
sockname.sin_family = AF_INET;
|
||||||
sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
|
sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
|
||||||
|
|
||||||
if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
|
if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
|
||||||
{
|
{
|
||||||
syslog(LOG_ERR, "bind(udp_notify): %m");
|
syslog(LOG_ERR, "bind(udp_notify): %m");
|
||||||
close(s);
|
close(s);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -13,18 +13,6 @@
|
|||||||
|
|
||||||
#define ROOTDESC_PATH "/rootDesc.xml"
|
#define ROOTDESC_PATH "/rootDesc.xml"
|
||||||
|
|
||||||
#ifdef HAS_DUMMY_SERVICE
|
|
||||||
#define DUMMY_PATH "/dummy.xml"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define WANCFG_PATH "/WANCfg.xml"
|
|
||||||
#define WANCFG_CONTROLURL "/ctl/CmnIfCfg"
|
|
||||||
#define WANCFG_EVENTURL "/evt/CmnIfCfg"
|
|
||||||
|
|
||||||
#define WANIPC_PATH "/WANIPCn.xml"
|
|
||||||
#define WANIPC_CONTROLURL "/ctl/IPConn"
|
|
||||||
#define WANIPC_EVENTURL "/evt/IPConn"
|
|
||||||
|
|
||||||
#define CONTENTDIRECTORY_PATH "/ContentDir.xml"
|
#define CONTENTDIRECTORY_PATH "/ContentDir.xml"
|
||||||
#define CONTENTDIRECTORY_CONTROLURL "/ctl/ContentDir"
|
#define CONTENTDIRECTORY_CONTROLURL "/ctl/ContentDir"
|
||||||
#define CONTENTDIRECTORY_EVENTURL "/evt/ContentDir"
|
#define CONTENTDIRECTORY_EVENTURL "/evt/ContentDir"
|
||||||
@ -37,11 +25,5 @@
|
|||||||
#define X_MS_MEDIARECEIVERREGISTRAR_CONTROLURL "/ctl/X_MS_MediaReceiverRegistrar"
|
#define X_MS_MEDIARECEIVERREGISTRAR_CONTROLURL "/ctl/X_MS_MediaReceiverRegistrar"
|
||||||
#define X_MS_MEDIARECEIVERREGISTRAR_EVENTURL "/evt/X_MS_MediaReceiverRegistrar"
|
#define X_MS_MEDIARECEIVERREGISTRAR_EVENTURL "/evt/X_MS_MediaReceiverRegistrar"
|
||||||
|
|
||||||
#ifdef ENABLE_L3F_SERVICE
|
|
||||||
#define L3F_PATH "/L3F.xml"
|
|
||||||
#define L3F_CONTROLURL "/ctl/L3F"
|
|
||||||
#define L3F_EVENTURL "/evt/L3F"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
11
options.c
11
options.c
@ -36,20 +36,11 @@ static const struct {
|
|||||||
{ UPNPMODEL_NUMBER, "model_number"},
|
{ UPNPMODEL_NUMBER, "model_number"},
|
||||||
{ UPNPCLEANTHRESHOLD, "clean_ruleset_threshold"},
|
{ UPNPCLEANTHRESHOLD, "clean_ruleset_threshold"},
|
||||||
{ UPNPCLEANINTERVAL, "clean_ruleset_interval"},
|
{ UPNPCLEANINTERVAL, "clean_ruleset_interval"},
|
||||||
#ifdef ENABLE_NATPMP
|
|
||||||
{ UPNPENABLENATPMP, "enable_natpmp"},
|
|
||||||
#endif
|
|
||||||
{ UPNPENABLE, "enable_upnp"},
|
{ UPNPENABLE, "enable_upnp"},
|
||||||
#ifdef USE_PF
|
|
||||||
{ UPNPQUEUE, "queue"},
|
|
||||||
{ UPNPTAG, "tag"},
|
|
||||||
#endif
|
|
||||||
#ifdef PF_ENABLE_FILTER_RULES
|
|
||||||
{ UPNPQUICKRULES, "quickrules" },
|
|
||||||
#endif
|
|
||||||
#ifdef ENABLE_LEASEFILE
|
#ifdef ENABLE_LEASEFILE
|
||||||
{ UPNPLEASEFILE, "lease_file"},
|
{ UPNPLEASEFILE, "lease_file"},
|
||||||
#endif
|
#endif
|
||||||
|
{ UPNPFRIENDLYNAME, "friendly_name"},
|
||||||
{ UPNPMEDIADIR, "media_dir"},
|
{ UPNPMEDIADIR, "media_dir"},
|
||||||
{ UPNPSECUREMODE, "secure_mode"}
|
{ UPNPSECUREMODE, "secure_mode"}
|
||||||
};
|
};
|
||||||
|
@ -29,17 +29,11 @@ enum upnpconfigoptions {
|
|||||||
UPNPCLEANTHRESHOLD, /* clean_ruleset_threshold */
|
UPNPCLEANTHRESHOLD, /* clean_ruleset_threshold */
|
||||||
UPNPCLEANINTERVAL, /* clean_ruleset_interval */
|
UPNPCLEANINTERVAL, /* clean_ruleset_interval */
|
||||||
UPNPENABLENATPMP, /* enable_natpmp */
|
UPNPENABLENATPMP, /* enable_natpmp */
|
||||||
#ifdef USE_PF
|
|
||||||
UPNPQUEUE, /* queue */
|
|
||||||
UPNPTAG, /* tag */
|
|
||||||
#endif
|
|
||||||
#ifdef PF_ENABLE_FILTER_RULES
|
|
||||||
UPNPQUICKRULES, /* quickrules */
|
|
||||||
#endif
|
|
||||||
UPNPSECUREMODE, /* secure_mode */
|
UPNPSECUREMODE, /* secure_mode */
|
||||||
#ifdef ENABLE_LEASEFILE
|
#ifdef ENABLE_LEASEFILE
|
||||||
UPNPLEASEFILE, /* lease_file */
|
UPNPLEASEFILE, /* lease_file */
|
||||||
#endif
|
#endif
|
||||||
|
UPNPFRIENDLYNAME, /* how the system should show up to DLNA clients */
|
||||||
UPNPMEDIADIR, /* directory to search for UPnP-A/V content */
|
UPNPMEDIADIR, /* directory to search for UPnP-A/V content */
|
||||||
UPNPENABLE /* enable_upnp */
|
UPNPENABLE /* enable_upnp */
|
||||||
};
|
};
|
||||||
|
108
scanner.c
108
scanner.c
@ -33,14 +33,19 @@
|
|||||||
int
|
int
|
||||||
is_video(const char * file)
|
is_video(const char * file)
|
||||||
{
|
{
|
||||||
return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
|
return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
|
||||||
ends_with(file, ".ts") || ends_with(file, ".avi"));
|
ends_with(file, ".asf") || ends_with(file, ".wmv") ||
|
||||||
|
ends_with(file, ".mp4") || ends_with(file, ".m4v") ||
|
||||||
|
ends_with(file, ".mts") || ends_with(file, ".m2ts") ||
|
||||||
|
ends_with(file, ".vob") || ends_with(file, ".ts") ||
|
||||||
|
ends_with(file, ".avi") || ends_with(file, ".xvid"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
is_audio(const char * file)
|
is_audio(const char * file)
|
||||||
{
|
{
|
||||||
return (ends_with(file, ".mp3") ||
|
return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
|
||||||
|
ends_with(file, ".fla") || ends_with(file, ".flc") ||
|
||||||
ends_with(file, ".m4a") || ends_with(file, ".aac"));
|
ends_with(file, ".m4a") || ends_with(file, ".aac"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,21 +129,27 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
|||||||
|
|
||||||
if( strstr(class, "imageItem") )
|
if( strstr(class, "imageItem") )
|
||||||
{
|
{
|
||||||
char *date = result[12+cols], *cam = result[16+cols];
|
char *date = result[13+cols], *cam = result[16+cols];
|
||||||
char date_taken[11]; date_taken[10] = '\0';
|
char date_taken[11]; date_taken[10] = '\0';
|
||||||
static int last_all_objectID = 0;
|
static int last_all_objectID = 0;
|
||||||
if( date )
|
|
||||||
strncpy(date_taken, date, 10);
|
|
||||||
if( date )
|
if( date )
|
||||||
{
|
{
|
||||||
|
if( strcmp(date, "0000-00-00") == 0 )
|
||||||
|
{
|
||||||
|
strcpy(date_taken, "Unknown");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strncpy(date_taken, date, 10);
|
||||||
|
}
|
||||||
container = insert_container("DATES", date_taken, "3$12", NULL, "album.photoAlbum", NULL);
|
container = insert_container("DATES", date_taken, "3$12", NULL, "album.photoAlbum", NULL);
|
||||||
parentID = container>>32;
|
parentID = container>>32;
|
||||||
objectID = container;
|
objectID = container;
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('3$12$%X$%X', '3$12$%X', '%s', '%s', %lu, %Q, %Q)",
|
" ('3$12$%X$%X', '3$12$%X', '%s', '%s', %lu, %Q)",
|
||||||
parentID, objectID, parentID, refID, class, detailID, path, name);
|
parentID, objectID, parentID, refID, class, detailID, name);
|
||||||
sql_exec(db, sql);
|
sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
}
|
}
|
||||||
@ -153,26 +164,26 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
|||||||
int subParentID = subcontainer>>32;
|
int subParentID = subcontainer>>32;
|
||||||
int subObjectID = subcontainer;
|
int subObjectID = subcontainer;
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('3$13$%X$%X$%X', '3$13$%X$%X', '%s', '%s', %lu, %Q, %Q)",
|
" ('3$13$%X$%X$%X', '3$13$%X$%X', '%s', '%s', %lu, %Q)",
|
||||||
parentID, subParentID, subObjectID, parentID, subParentID, refID, class, detailID, path, name);
|
parentID, subParentID, subObjectID, parentID, subParentID, refID, class, detailID, name);
|
||||||
sql_exec(db, sql);
|
sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
}
|
}
|
||||||
/* All Images */
|
/* All Images */
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('3$11$%X', '3$11', '%s', '%s', %lu, %Q, %Q)",
|
" ('3$11$%X', '3$11', '%s', '%s', %lu, %Q)",
|
||||||
last_all_objectID++, refID, class, detailID, path, name);
|
last_all_objectID++, refID, class, detailID, name);
|
||||||
sql_exec(db, sql);
|
sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
}
|
}
|
||||||
else if( strstr(class, "audioItem") )
|
else if( strstr(class, "audioItem") )
|
||||||
{
|
{
|
||||||
char *artist = result[6+cols], *album = result[7+cols], *genre = result[8+cols];
|
char *artist = cols ? result[7+cols]:NULL, *album = cols ? result[8+cols]:NULL, *genre = cols ? result[9+cols]:NULL;
|
||||||
static char last_artist[1024];
|
static char last_artist[1024] = "0";
|
||||||
static int last_artist_parentID, last_artist_objectID;
|
static int last_artist_parentID, last_artist_objectID;
|
||||||
static char last_album[1024];
|
static char last_album[1024];
|
||||||
static int last_album_parentID, last_album_objectID;
|
static int last_album_parentID, last_album_objectID;
|
||||||
@ -197,10 +208,10 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
|||||||
last_artist_parentID = parentID;
|
last_artist_parentID = parentID;
|
||||||
}
|
}
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('1$6$%X$%X', '1$6$%X', '%s', '%s', %lu, %Q, %Q)",
|
" ('1$6$%X$%X', '1$6$%X', '%s', '%s', %lu, %Q)",
|
||||||
parentID, objectID, parentID, refID, class, detailID, path, name);
|
parentID, objectID, parentID, refID, class, detailID, name);
|
||||||
sql_exec(db, sql);
|
sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
}
|
}
|
||||||
@ -221,10 +232,10 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
|||||||
last_album_parentID = parentID;
|
last_album_parentID = parentID;
|
||||||
}
|
}
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('1$7$%X$%X', '1$7$%X', '%s', '%s', %lu, %Q, %Q)",
|
" ('1$7$%X$%X', '1$7$%X', '%s', '%s', %lu, %Q)",
|
||||||
parentID, objectID, parentID, refID, class, detailID, path, name);
|
parentID, objectID, parentID, refID, class, detailID, name);
|
||||||
sql_exec(db, sql);
|
sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
}
|
}
|
||||||
@ -245,19 +256,19 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
|||||||
last_genre_parentID = parentID;
|
last_genre_parentID = parentID;
|
||||||
}
|
}
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('1$5$%X$%X', '1$5$%X', '%s', '%s', %lu, %Q, %Q)",
|
" ('1$5$%X$%X', '1$5$%X', '%s', '%s', %lu, %Q)",
|
||||||
parentID, objectID, parentID, refID, class, detailID, path, name);
|
parentID, objectID, parentID, refID, class, detailID, name);
|
||||||
sql_exec(db, sql);
|
sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
}
|
}
|
||||||
/* All Music */
|
/* All Music */
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('1$4$%X', '1$4', '%s', '%s', %lu, %Q, %Q)",
|
" ('1$4$%X', '1$4', '%s', '%s', %lu, %Q)",
|
||||||
last_all_objectID++, refID, class, detailID, path, name);
|
last_all_objectID++, refID, class, detailID, name);
|
||||||
sql_exec(db, sql);
|
sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
}
|
}
|
||||||
@ -278,10 +289,10 @@ insert_directory(const char * name, const char * path, const char * parentID, in
|
|||||||
for( i=0; base[i]; i++ )
|
for( i=0; base[i]; i++ )
|
||||||
{
|
{
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, PATH, NAME) "
|
" (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('%s%s$%X', '%s%s', %Q, '%lld', '%s', '%q', '%q')",
|
" ('%s%s$%X', '%s%s', %Q, '%lld', '%s', '%q')",
|
||||||
base[i], parentID, objectID, base[i], parentID, refID, detailID, class, path, name);
|
base[i], parentID, objectID, base[i], parentID, refID, detailID, class, name);
|
||||||
//DEBUG printf("SQL: %s\n", sql);
|
//DEBUG printf("SQL: %s\n", sql);
|
||||||
ret = sql_exec(db, sql);
|
ret = sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
@ -327,12 +338,14 @@ insert_file(char * name, const char * path, const char * parentID, int object)
|
|||||||
detailID = GetVideoMetadata(path, name);
|
detailID = GetVideoMetadata(path, name);
|
||||||
}
|
}
|
||||||
//DEBUG printf("Got DetailID %lu!\n", detailID);
|
//DEBUG printf("Got DetailID %lu!\n", detailID);
|
||||||
|
if( !detailID )
|
||||||
|
return -1;
|
||||||
|
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
" (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
" (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('%s', '%s%s', '%s', %lu, '%q', '%q')",
|
" ('%s', '%s%s', '%s', %lu, '%q')",
|
||||||
objectID, BROWSEDIR_ID, parentID, class, detailID, path, name);
|
objectID, BROWSEDIR_ID, parentID, class, detailID, name);
|
||||||
//DEBUG printf("SQL: %s\n", sql);
|
//DEBUG printf("SQL: %s\n", sql);
|
||||||
sql_exec(db, sql);
|
sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
@ -378,6 +391,10 @@ create_database(void)
|
|||||||
sql_exec(db, "pragma synchronous = OFF;");
|
sql_exec(db, "pragma synchronous = OFF;");
|
||||||
sql_exec(db, "pragma cache_size = 8192;");
|
sql_exec(db, "pragma cache_size = 8192;");
|
||||||
|
|
||||||
|
//JM: Set up a db version number, so we know if we need to rebuild due to a new structure.
|
||||||
|
sprintf(sql_buf, "pragma user_version = %d;", DB_VERSION);
|
||||||
|
sql_exec(db, sql_buf);
|
||||||
|
|
||||||
ret = sql_exec(db, "CREATE TABLE OBJECTS ( "
|
ret = sql_exec(db, "CREATE TABLE OBJECTS ( "
|
||||||
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
|
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
"OBJECT_ID TEXT NOT NULL, "
|
"OBJECT_ID TEXT NOT NULL, "
|
||||||
@ -385,13 +402,13 @@ create_database(void)
|
|||||||
"REF_ID TEXT DEFAULT NULL, "
|
"REF_ID TEXT DEFAULT NULL, "
|
||||||
"CLASS TEXT NOT NULL, "
|
"CLASS TEXT NOT NULL, "
|
||||||
"DETAIL_ID INTEGER DEFAULT NULL, "
|
"DETAIL_ID INTEGER DEFAULT NULL, "
|
||||||
"PATH TEXT DEFAULT NULL, "
|
|
||||||
"NAME TEXT DEFAULT NULL"
|
"NAME TEXT DEFAULT NULL"
|
||||||
");");
|
");");
|
||||||
if( ret != SQLITE_OK )
|
if( ret != SQLITE_OK )
|
||||||
goto sql_failed;
|
goto sql_failed;
|
||||||
ret = sql_exec(db, "CREATE TABLE DETAILS ( "
|
ret = sql_exec(db, "CREATE TABLE DETAILS ( "
|
||||||
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
|
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
|
"PATH TEXT DEFAULT NULL, "
|
||||||
"SIZE INTEGER, "
|
"SIZE INTEGER, "
|
||||||
"TITLE TEXT, "
|
"TITLE TEXT, "
|
||||||
"DURATION TEXT, "
|
"DURATION TEXT, "
|
||||||
@ -433,7 +450,7 @@ create_database(void)
|
|||||||
sql_exec(db, "create INDEX IDX_OBJECTS_PARENT_ID ON OBJECTS(PARENT_ID);");
|
sql_exec(db, "create INDEX IDX_OBJECTS_PARENT_ID ON OBJECTS(PARENT_ID);");
|
||||||
sql_exec(db, "create INDEX IDX_OBJECTS_DETAIL_ID ON OBJECTS(DETAIL_ID);");
|
sql_exec(db, "create INDEX IDX_OBJECTS_DETAIL_ID ON OBJECTS(DETAIL_ID);");
|
||||||
sql_exec(db, "create INDEX IDX_OBJECTS_CLASS ON OBJECTS(CLASS);");
|
sql_exec(db, "create INDEX IDX_OBJECTS_CLASS ON OBJECTS(CLASS);");
|
||||||
sql_exec(db, "create INDEX IDX_OBJECTS_PATH ON OBJECTS(PATH);");
|
sql_exec(db, "create INDEX IDX_DETAILS_PATH ON DETAILS(PATH);");
|
||||||
sql_exec(db, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
|
sql_exec(db, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
|
||||||
|
|
||||||
|
|
||||||
@ -467,6 +484,9 @@ ScanDirectory(const char * dir, const char * parent)
|
|||||||
char parent_id[PATH_MAX];
|
char parent_id[PATH_MAX];
|
||||||
char full_path[PATH_MAX];
|
char full_path[PATH_MAX];
|
||||||
char * name;
|
char * name;
|
||||||
|
#if USE_FORK
|
||||||
|
pid_t newpid;
|
||||||
|
#endif
|
||||||
|
|
||||||
if( !parent )
|
if( !parent )
|
||||||
{
|
{
|
||||||
@ -475,12 +495,17 @@ ScanDirectory(const char * dir, const char * parent)
|
|||||||
fprintf(stderr, "Error creating database!\n");
|
fprintf(stderr, "Error creating database!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#if USE_FORK
|
||||||
|
newpid = fork();
|
||||||
|
if( newpid )
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
setlocale(LC_COLLATE, "");
|
setlocale(LC_COLLATE, "");
|
||||||
if( chdir(dir) != 0 )
|
if( chdir(dir) != 0 )
|
||||||
return;
|
return;
|
||||||
printf("\nScanning %s\n", dir);
|
printf("\nScanning %s\n", dir);
|
||||||
n = scandir(".", &namelist, filter_media, alphasort);
|
n = scandir(".", &namelist, filter_media, alphasort);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
fprintf(stderr, "Error scanning %s [scandir]\n", dir);
|
fprintf(stderr, "Error scanning %s [scandir]\n", dir);
|
||||||
@ -512,4 +537,11 @@ printf("\nScanning %s\n", dir);
|
|||||||
}
|
}
|
||||||
free(namelist);
|
free(namelist);
|
||||||
chdir("..");
|
chdir("..");
|
||||||
|
if( !parent )
|
||||||
|
{
|
||||||
|
printf("Scanning \"%s\" finished!\n", dir);
|
||||||
|
#if USE_FORK
|
||||||
|
_exit(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,12 @@
|
|||||||
#include "upnpdescgen.h"
|
#include "upnpdescgen.h"
|
||||||
|
|
||||||
char uuidvalue[] = "uuid:12345678-0000-0000-0000-00000000abcd";
|
char uuidvalue[] = "uuid:12345678-0000-0000-0000-00000000abcd";
|
||||||
|
char friendly_name[] = "localhost: system_type";
|
||||||
char serialnumber[] = "12345678";
|
char serialnumber[] = "12345678";
|
||||||
char modelnumber[] = "1";
|
char modelnumber[] = "1";
|
||||||
char presentationurl[] = "http://192.168.0.1:8080/";
|
char presentationurl[] = "http://192.168.0.1:8080/";
|
||||||
|
|
||||||
char * use_ext_ip_addr = NULL;
|
char * use_ext_ip_addr = NULL;
|
||||||
const char * ext_if_name = "eth0";
|
|
||||||
|
|
||||||
int getifaddr(const char * ifname, char * buf, int len)
|
int getifaddr(const char * ifname, char * buf, int len)
|
||||||
{
|
{
|
||||||
|
@ -85,10 +85,14 @@ static const char * const upnpallowedvalues[] =
|
|||||||
"STOPPED",
|
"STOPPED",
|
||||||
0,
|
0,
|
||||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," /* 44 */
|
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," /* 44 */
|
||||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01,"
|
"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,"
|
"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,"
|
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC,"
|
"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/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:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||||
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
|
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
|
||||||
"http-get:*:audio/x-ms-wma:*,"
|
"http-get:*:audio/x-ms-wma:*,"
|
||||||
"http-get:*:audio/wav:*,"
|
"http-get:*:audio/wav:*,"
|
||||||
@ -129,7 +133,7 @@ static const struct XMLElt rootDesc[] =
|
|||||||
{"/major", "1"},
|
{"/major", "1"},
|
||||||
{"/minor", "0"},
|
{"/minor", "0"},
|
||||||
{"/deviceType", "urn:schemas-upnp-org:device:MediaServer:1"},
|
{"/deviceType", "urn:schemas-upnp-org:device:MediaServer:1"},
|
||||||
{"/friendlyName", ROOTDEV_FRIENDLYNAME}, /* required */
|
{"/friendlyName", friendly_name}, /* required */
|
||||||
{"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */
|
{"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */
|
||||||
{"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */
|
{"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */
|
||||||
{"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */
|
{"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */
|
||||||
@ -175,12 +179,6 @@ static const struct argument AddPortMappingArgs[] =
|
|||||||
{NULL, 0, 0}
|
{NULL, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct argument GetExternalIPAddressArgs[] =
|
|
||||||
{
|
|
||||||
{NULL, 2, 7},
|
|
||||||
{NULL, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct argument DeletePortMappingArgs[] =
|
static const struct argument DeletePortMappingArgs[] =
|
||||||
{
|
{
|
||||||
{NULL, 1, 11},
|
{NULL, 1, 11},
|
||||||
@ -202,14 +200,6 @@ static const struct argument GetConnectionTypeInfoArgs[] =
|
|||||||
{NULL, 0, 0}
|
{NULL, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct argument GetStatusInfoArgs[] =
|
|
||||||
{
|
|
||||||
{NULL, 2, 2},
|
|
||||||
{NULL, 2, 4},
|
|
||||||
{NULL, 2, 3},
|
|
||||||
{NULL, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct argument GetNATRSIPStatusArgs[] =
|
static const struct argument GetNATRSIPStatusArgs[] =
|
||||||
{
|
{
|
||||||
{NULL, 2, 5},
|
{NULL, 2, 5},
|
||||||
@ -494,71 +484,6 @@ static const struct argument GetTotalPacketsReceivedArgs[] =
|
|||||||
{NULL, 0, 0}
|
{NULL, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct action WANCfgActions[] =
|
|
||||||
{
|
|
||||||
{"GetCommonLinkProperties", GetCommonLinkPropertiesArgs}, /* Required */
|
|
||||||
{"GetTotalBytesSent", GetTotalBytesSentArgs}, /* optional */
|
|
||||||
{"GetTotalBytesReceived", GetTotalBytesReceivedArgs}, /* optional */
|
|
||||||
{"GetTotalPacketsSent", GetTotalPacketsSentArgs}, /* optional */
|
|
||||||
{"GetTotalPacketsReceived", GetTotalPacketsReceivedArgs}, /* optional */
|
|
||||||
{0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */
|
|
||||||
static const struct stateVar WANCfgVars[] =
|
|
||||||
{
|
|
||||||
{"WANAccessType", 0, 0, 1},
|
|
||||||
/* Allowed Values : DSL / POTS / Cable / Ethernet
|
|
||||||
* Default value : empty string */
|
|
||||||
{"Layer1UpstreamMaxBitRate", 3, 0},
|
|
||||||
{"Layer1DownstreamMaxBitRate", 3, 0},
|
|
||||||
{"PhysicalLinkStatus", 0|0x80, 0, 6, 6},
|
|
||||||
/* allowed values :
|
|
||||||
* Up / Down / Initializing (optional) / Unavailable (optionnal)
|
|
||||||
* no Default value
|
|
||||||
* Evented */
|
|
||||||
{"TotalBytesSent", 3, 0}, /* Optional */
|
|
||||||
{"TotalBytesReceived", 3, 0}, /* Optional */
|
|
||||||
{"TotalPacketsSent", 3, 0}, /* Optional */
|
|
||||||
{"TotalPacketsReceived", 3, 0},/* Optional */
|
|
||||||
/*{"MaximumActiveConnections", 2, 0}, // allowed Range value // OPTIONAL */
|
|
||||||
{0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct serviceDesc scpdWANCfg =
|
|
||||||
{ WANCfgActions, WANCfgVars };
|
|
||||||
|
|
||||||
#ifdef ENABLE_L3F_SERVICE
|
|
||||||
/* Read UPnP_IGD_Layer3Forwarding_1.0.pdf */
|
|
||||||
static const struct argument SetDefaultConnectionServiceArgs[] =
|
|
||||||
{
|
|
||||||
{NULL, 1, 0}, /* in */
|
|
||||||
{NULL, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct argument GetDefaultConnectionServiceArgs[] =
|
|
||||||
{
|
|
||||||
{NULL, 2, 0}, /* out */
|
|
||||||
{0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct action L3FActions[] =
|
|
||||||
{
|
|
||||||
{"SetDefaultConnectionService", SetDefaultConnectionServiceArgs}, /* Req */
|
|
||||||
{"GetDefaultConnectionService", GetDefaultConnectionServiceArgs}, /* Req */
|
|
||||||
{0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct stateVar L3FVars[] =
|
|
||||||
{
|
|
||||||
{"DefaultConnectionService", 0|0x80, 0, 0, 255}, /* Required */
|
|
||||||
{0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct serviceDesc scpdL3F =
|
|
||||||
{ L3FActions, L3FVars };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const struct serviceDesc scpdContentDirectory =
|
static const struct serviceDesc scpdContentDirectory =
|
||||||
{ ContentDirectoryActions, ContentDirectoryVars };
|
{ ContentDirectoryActions, ContentDirectoryVars };
|
||||||
//{ ContentDirectoryActions, ContentDirectoryVars };
|
//{ ContentDirectoryActions, ContentDirectoryVars };
|
||||||
@ -709,9 +634,7 @@ genRootDesc(int * len)
|
|||||||
char *ret = calloc(1, 8192);
|
char *ret = calloc(1, 8192);
|
||||||
sprintf(ret, "<?xml version='1.0' encoding='UTF-8' ?>\r\n"
|
sprintf(ret, "<?xml version='1.0' encoding='UTF-8' ?>\r\n"
|
||||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType><friendlyName>MiniDLNA (MaggardMachine2)</friendlyName><manufacturer>NETGEAR</manufacturer><manufacturerURL>http://www.netgear.com</manufacturerURL><modelDescription>NETGEAR ReadyNAS NV</modelDescription><modelName>ReadyNAS</modelName><modelNumber>NV</modelNumber><modelURL>http://www.netgear.com</modelURL><UDN>uuid:aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d</UDN><dlna:X_DLNADOC>DMS-1.50</dlna:X_DLNADOC>\r\n"
|
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType><friendlyName>MiniDLNA (MaggardMachine2)</friendlyName><manufacturer>NETGEAR</manufacturer><manufacturerURL>http://www.netgear.com</manufacturerURL><modelDescription>NETGEAR ReadyNAS NV</modelDescription><modelName>ReadyNAS</modelName><modelNumber>NV</modelNumber><modelURL>http://www.netgear.com</modelURL><UDN>uuid:aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d</UDN><dlna:X_DLNADOC>DMS-1.50</dlna:X_DLNADOC>\r\n"
|
||||||
//"<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType><friendlyName>MiniDLNA (MaggardMachine2)</friendlyName><manufacturer>NETGEAR</manufacturer><manufacturerURL>http://www.netgear.com</manufacturerURL><modelDescription>NETGEAR ReadyNAS NV</modelDescription><modelName>ReadyNAS</modelName><modelNumber>NV</modelNumber><modelURL>http://www.netgear.com</modelURL><UDN>uuid:aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d</UDN><dlna:X_DLNACAP>av-upload,image-upload,create-child-container,audio-upload</dlna:X_DLNACAP><dlna:X_DLNADOC>DMS-1.50</dlna:X_DLNADOC>\r\n"
|
|
||||||
"<serviceList><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionMgr.xml</SCPDURL><controlURL>/ctl/ConnectionMgr</controlURL><eventSubURL>/evt/ConnectionMgr</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType><serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId><SCPDURL>/ContentDir.xml</SCPDURL><controlURL>/ctl/ContentDir</controlURL><eventSubURL>/evt/ContentDir</eventSubURL></service></serviceList></device></root>");
|
"<serviceList><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionMgr.xml</SCPDURL><controlURL>/ctl/ConnectionMgr</controlURL><eventSubURL>/evt/ConnectionMgr</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType><serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId><SCPDURL>/ContentDir.xml</SCPDURL><controlURL>/ctl/ContentDir</controlURL><eventSubURL>/evt/ContentDir</eventSubURL></service></serviceList></device></root>");
|
||||||
//"</iconList><serviceList><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionMgr.xml</SCPDURL><controlURL>/ctl/ConnectionMgr</controlURL><eventSubURL>/evt/ConnectionMgr</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType><serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId><SCPDURL>/ContentDir.xml</SCPDURL><controlURL>/ctl/ContentDir</controlURL><eventSubURL>/evt/ContentDir</eventSubURL></service><service><serviceType>urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1</serviceType><serviceId>763f907c-8cfb-11dd-a382-c9c0ad9eae41</serviceId><SCPDURL>/upnp/aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d/aefdc437-8cf7-11dd-b3bb-ff0d6f9a7e6d</SCPDURL><controlURL>/upnp/aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d/control/aefdc437-8cf7-11dd-b3bb-ff0d6f9a7e6d</controlURL><eventSubURL>/upnp/aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d/events/aefdc437-8cf7-11dd-b3bb-ff0d6f9a7e6d</eventSubURL></service></serviceList></device></root>\r\n");
|
|
||||||
* len = strlen(ret);
|
* len = strlen(ret);
|
||||||
return ret;
|
return ret;
|
||||||
#endif
|
#endif
|
||||||
|
@ -68,11 +68,6 @@ genWANIPCn(int * len);
|
|||||||
char *
|
char *
|
||||||
genWANCfg(int * len);
|
genWANCfg(int * len);
|
||||||
|
|
||||||
#ifdef ENABLE_L3F_SERVICE
|
|
||||||
char *
|
|
||||||
genL3F(int * len);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ENABLE_EVENTS
|
#ifdef ENABLE_EVENTS
|
||||||
char *
|
char *
|
||||||
getVarsContentDirectory(int * len);
|
getVarsContentDirectory(int * len);
|
||||||
@ -81,13 +76,7 @@ char *
|
|||||||
getVarsConnectionManager(int * len);
|
getVarsConnectionManager(int * len);
|
||||||
|
|
||||||
char *
|
char *
|
||||||
getVarsWANIPCn(int * len);
|
getVarsX_MS_MediaReceiverRegistrar(int * len);
|
||||||
|
|
||||||
char *
|
|
||||||
getVarsWANCfg(int * len);
|
|
||||||
|
|
||||||
char *
|
|
||||||
getVarsL3F(int * len);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -9,30 +9,14 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
/* strings used in the root device xml description */
|
/* strings used in the root device xml description */
|
||||||
#define ROOTDEV_FRIENDLYNAME "MiniDLNA ReadyNAS:"
|
#ifdef READYNAS
|
||||||
#define ROOTDEV_MANUFACTURER "NETGEAR"
|
#define ROOTDEV_MANUFACTURER "NETGEAR"
|
||||||
|
#else
|
||||||
|
#define ROOTDEV_MANUFACTURER "Justin Maggard"
|
||||||
|
#endif
|
||||||
#define ROOTDEV_MANUFACTURERURL OS_URL
|
#define ROOTDEV_MANUFACTURERURL OS_URL
|
||||||
#define ROOTDEV_MODELNAME "Windows Media Connect compatible (minidlna)"
|
#define ROOTDEV_MODELNAME "Windows Media Connect compatible (minidlna)"
|
||||||
#define ROOTDEV_MODELDESCRIPTION OS_NAME " *ReadyNAS dev DLNA"
|
#define ROOTDEV_MODELDESCRIPTION OS_NAME " *ReadyNAS dev DLNA"
|
||||||
#define ROOTDEV_MODELURL OS_URL
|
#define ROOTDEV_MODELURL OS_URL
|
||||||
|
|
||||||
#define WANDEV_FRIENDLYNAME "WANDevice"
|
|
||||||
#define WANDEV_MANUFACTURER "MiniUPnP"
|
|
||||||
#define WANDEV_MANUFACTURERURL "http://miniupnp.free.fr/"
|
|
||||||
#define WANDEV_MODELNAME "WAN Device"
|
|
||||||
#define WANDEV_MODELDESCRIPTION "WAN Device"
|
|
||||||
#define WANDEV_MODELNUMBER UPNP_VERSION
|
|
||||||
#define WANDEV_MODELURL "http://miniupnp.free.fr/"
|
|
||||||
#define WANDEV_UPC "MINIUPNPD"
|
|
||||||
|
|
||||||
#define WANCDEV_FRIENDLYNAME "WANConnectionDevice"
|
|
||||||
#define WANCDEV_MANUFACTURER WANDEV_MANUFACTURER
|
|
||||||
#define WANCDEV_MANUFACTURERURL WANDEV_MANUFACTURERURL
|
|
||||||
#define WANCDEV_MODELNAME "MiniUPnPd"
|
|
||||||
#define WANCDEV_MODELDESCRIPTION "MiniUPnP daemon"
|
|
||||||
#define WANCDEV_MODELNUMBER UPNP_VERSION
|
|
||||||
#define WANCDEV_MODELURL "http://miniupnp.free.fr/"
|
|
||||||
#define WANCDEV_UPC "MINIUPNPD"
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
32
upnpevents.c
32
upnpevents.c
@ -24,6 +24,12 @@
|
|||||||
#include "upnpglobalvars.h"
|
#include "upnpglobalvars.h"
|
||||||
#include "upnpdescgen.h"
|
#include "upnpdescgen.h"
|
||||||
|
|
||||||
|
#define HAVE_UUID 1
|
||||||
|
|
||||||
|
#ifdef HAVE_UUID
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_EVENTS
|
#ifdef ENABLE_EVENTS
|
||||||
/*enum subscriber_service_enum {
|
/*enum subscriber_service_enum {
|
||||||
EWanCFG = 1,
|
EWanCFG = 1,
|
||||||
@ -80,18 +86,12 @@ newSubscriber(const char * eventurl, const char * callback, int callbacklen)
|
|||||||
if(!eventurl || !callback || !callbacklen)
|
if(!eventurl || !callback || !callbacklen)
|
||||||
return NULL;
|
return NULL;
|
||||||
tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
|
tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
|
||||||
if(strcmp(eventurl, WANCFG_EVENTURL)==0)
|
if(strcmp(eventurl, CONTENTDIRECTORY_EVENTURL)==0)
|
||||||
tmp->service = EWanCFG;
|
|
||||||
else if(strcmp(eventurl, CONTENTDIRECTORY_EVENTURL)==0)
|
|
||||||
tmp->service = EContentDirectory;
|
tmp->service = EContentDirectory;
|
||||||
else if(strcmp(eventurl, CONNECTIONMGR_EVENTURL)==0)
|
else if(strcmp(eventurl, CONNECTIONMGR_EVENTURL)==0)
|
||||||
tmp->service = EConnectionManager;
|
tmp->service = EConnectionManager;
|
||||||
else if(strcmp(eventurl, WANIPC_EVENTURL)==0)
|
else if(strcmp(eventurl, X_MS_MEDIARECEIVERREGISTRAR_EVENTURL)==0)
|
||||||
tmp->service = EWanIPC;
|
tmp->service = EMSMediaReceiverRegistrar;
|
||||||
#ifdef ENABLE_L3F_SERVICE
|
|
||||||
else if(strcmp(eventurl, L3F_EVENTURL)==0)
|
|
||||||
tmp->service = EL3F;
|
|
||||||
#endif
|
|
||||||
else {
|
else {
|
||||||
free(tmp);
|
free(tmp);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -99,10 +99,15 @@ newSubscriber(const char * eventurl, const char * callback, int callbacklen)
|
|||||||
memcpy(tmp->callback, callback, callbacklen);
|
memcpy(tmp->callback, callback, callbacklen);
|
||||||
tmp->callback[callbacklen] = '\0';
|
tmp->callback[callbacklen] = '\0';
|
||||||
/* make a dummy uuid */
|
/* make a dummy uuid */
|
||||||
/* TODO: improve that */
|
|
||||||
strncpy(tmp->uuid, uuidvalue, sizeof(tmp->uuid));
|
strncpy(tmp->uuid, uuidvalue, sizeof(tmp->uuid));
|
||||||
|
#ifdef HAVE_UUID
|
||||||
|
uuid_t uuid;
|
||||||
|
uuid_generate_time(uuid);
|
||||||
|
uuid_unparse_lower(uuid, tmp->uuid+5);
|
||||||
|
#else
|
||||||
tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
|
tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
|
||||||
snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
|
snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
|
||||||
|
#endif
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +141,7 @@ renewSubscription(const char * sid, int sidlen, int timeout)
|
|||||||
{
|
{
|
||||||
struct subscriber * sub;
|
struct subscriber * sub;
|
||||||
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
|
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
|
||||||
if(memcmp(sid, sub->uuid, 41)) {
|
if(memcmp(sid, sub->uuid, 41) == 0) {
|
||||||
sub->timeout = (timeout ? time(NULL) + timeout : 0);
|
sub->timeout = (timeout ? time(NULL) + timeout : 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -290,11 +295,14 @@ static void upnp_event_prepare(struct upnp_event_notify * obj)
|
|||||||
case EConnectionManager:
|
case EConnectionManager:
|
||||||
xml = getVarsConnectionManager(&l);
|
xml = getVarsConnectionManager(&l);
|
||||||
break;
|
break;
|
||||||
|
case EMSMediaReceiverRegistrar:
|
||||||
|
xml = getVarsX_MS_MediaReceiverRegistrar(&l);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
xml = NULL;
|
xml = NULL;
|
||||||
l = 0;
|
l = 0;
|
||||||
}
|
}
|
||||||
obj->buffersize = 1536;
|
obj->buffersize = 2048;
|
||||||
obj->buffer = malloc(obj->buffersize);
|
obj->buffer = malloc(obj->buffersize);
|
||||||
/*if(!obj->buffer) {
|
/*if(!obj->buffer) {
|
||||||
}*/
|
}*/
|
||||||
|
@ -8,11 +8,9 @@
|
|||||||
#define __UPNPEVENTS_H__
|
#define __UPNPEVENTS_H__
|
||||||
#ifdef ENABLE_EVENTS
|
#ifdef ENABLE_EVENTS
|
||||||
enum subscriber_service_enum {
|
enum subscriber_service_enum {
|
||||||
EWanCFG = 1,
|
EContentDirectory = 1,
|
||||||
EContentDirectory,
|
|
||||||
EConnectionManager,
|
EConnectionManager,
|
||||||
EWanIPC,
|
EMSMediaReceiverRegistrar
|
||||||
EL3F
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -11,9 +11,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "upnpglobalvars.h"
|
#include "upnpglobalvars.h"
|
||||||
|
|
||||||
/* network interface for internet */
|
|
||||||
const char * ext_if_name = 0;
|
|
||||||
|
|
||||||
/* file to store leases */
|
/* file to store leases */
|
||||||
#ifdef ENABLE_LEASEFILE
|
#ifdef ENABLE_LEASEFILE
|
||||||
const char* lease_file = 0;
|
const char* lease_file = 0;
|
||||||
@ -39,9 +36,6 @@ int sysuptime = 0;
|
|||||||
/* log packets flag */
|
/* log packets flag */
|
||||||
int logpackets = 0;
|
int logpackets = 0;
|
||||||
|
|
||||||
#ifdef ENABLE_NATPMP
|
|
||||||
int enablenatpmp = 0;
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int runtime_flags = 0;
|
int runtime_flags = 0;
|
||||||
@ -61,21 +55,10 @@ char presentationurl[PRESENTATIONURL_MAX_LEN];
|
|||||||
struct upnpperm * upnppermlist = 0;
|
struct upnpperm * upnppermlist = 0;
|
||||||
unsigned int num_upnpperm = 0;
|
unsigned int num_upnpperm = 0;
|
||||||
|
|
||||||
#ifdef ENABLE_NATPMP
|
|
||||||
/* NAT-PMP */
|
|
||||||
unsigned int nextnatpmptoclean_timestamp = 0;
|
|
||||||
unsigned short nextnatpmptoclean_eport = 0;
|
|
||||||
unsigned short nextnatpmptoclean_proto = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_PF
|
|
||||||
const char * queue = 0;
|
|
||||||
const char * tag = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int n_lan_addr = 0;
|
int n_lan_addr = 0;
|
||||||
struct lan_addr_s lan_addr[MAX_LAN_ADDR];
|
struct lan_addr_s lan_addr[MAX_LAN_ADDR];
|
||||||
|
|
||||||
/* UPnP-A/V [DLNA] */
|
/* UPnP-A/V [DLNA] */
|
||||||
sqlite3 *db;
|
sqlite3 *db;
|
||||||
char media_dir[256];
|
char media_dir[MEDIADIR_MAX_LEN];
|
||||||
|
char friendly_name[FRIENDLYNAME_MAX_LEN];
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
/* name of the network interface used to acces internet */
|
#define USE_FORK 1
|
||||||
extern const char * ext_if_name;
|
#define DB_VERSION 1
|
||||||
|
|
||||||
/* file to store all leases */
|
/* file to store all leases */
|
||||||
#ifdef ENABLE_LEASEFILE
|
#ifdef ENABLE_LEASEFILE
|
||||||
@ -36,16 +36,9 @@ extern time_t startup_time;
|
|||||||
extern int runtime_flags;
|
extern int runtime_flags;
|
||||||
#define LOGPACKETSMASK 0x0001
|
#define LOGPACKETSMASK 0x0001
|
||||||
#define SYSUPTIMEMASK 0x0002
|
#define SYSUPTIMEMASK 0x0002
|
||||||
#ifdef ENABLE_NATPMP
|
|
||||||
#define ENABLENATPMPMASK 0x0004
|
|
||||||
#endif
|
|
||||||
#define CHECKCLIENTIPMASK 0x0008
|
#define CHECKCLIENTIPMASK 0x0008
|
||||||
#define SECUREMODEMASK 0x0010
|
#define SECUREMODEMASK 0x0010
|
||||||
|
|
||||||
#ifdef PF_ENABLE_FILTER_RULES
|
|
||||||
#define PFNOQUICKRULESMASK 0x0040
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SETFLAG(mask) runtime_flags |= mask
|
#define SETFLAG(mask) runtime_flags |= mask
|
||||||
#define GETFLAG(mask) runtime_flags & mask
|
#define GETFLAG(mask) runtime_flags & mask
|
||||||
#define CLEARFLAG(mask) runtime_flags &= ~mask
|
#define CLEARFLAG(mask) runtime_flags &= ~mask
|
||||||
@ -67,19 +60,6 @@ extern char presentationurl[];
|
|||||||
extern struct upnpperm * upnppermlist;
|
extern struct upnpperm * upnppermlist;
|
||||||
extern unsigned int num_upnpperm;
|
extern unsigned int num_upnpperm;
|
||||||
|
|
||||||
#ifdef ENABLE_NATPMP
|
|
||||||
/* NAT-PMP */
|
|
||||||
extern unsigned int nextnatpmptoclean_timestamp;
|
|
||||||
extern unsigned short nextnatpmptoclean_eport;
|
|
||||||
extern unsigned short nextnatpmptoclean_proto;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_PF
|
|
||||||
/* queue and tag for PF rules */
|
|
||||||
extern const char * queue;
|
|
||||||
extern const char * tag;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* lan addresses */
|
/* lan addresses */
|
||||||
/* MAX_LAN_ADDR : maximum number of interfaces
|
/* MAX_LAN_ADDR : maximum number of interfaces
|
||||||
* to listen to SSDP traffic */
|
* to listen to SSDP traffic */
|
||||||
@ -91,5 +71,7 @@ extern struct lan_addr_s lan_addr[];
|
|||||||
extern sqlite3 *db;
|
extern sqlite3 *db;
|
||||||
#define MEDIADIR_MAX_LEN (256)
|
#define MEDIADIR_MAX_LEN (256)
|
||||||
extern char media_dir[];
|
extern char media_dir[];
|
||||||
|
#define FRIENDLYNAME_MAX_LEN (64)
|
||||||
|
extern char friendly_name[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
86
upnphttp.c
86
upnphttp.c
@ -367,25 +367,6 @@ findendheaders(const char * s, int len)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_DUMMY_SERVICE
|
|
||||||
static void
|
|
||||||
sendDummyDesc(struct upnphttp * h)
|
|
||||||
{
|
|
||||||
static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
|
|
||||||
"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
|
|
||||||
" <specVersion>"
|
|
||||||
" <major>1</major>"
|
|
||||||
" <minor>0</minor>"
|
|
||||||
" </specVersion>"
|
|
||||||
" <actionList />"
|
|
||||||
" <serviceStateTable />"
|
|
||||||
"</scpd>\r\n";
|
|
||||||
BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
|
|
||||||
SendResp_upnphttp(h);
|
|
||||||
CloseSocket_upnphttp(h);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Sends the description generated by the parameter */
|
/* Sends the description generated by the parameter */
|
||||||
static void
|
static void
|
||||||
sendXMLdesc(struct upnphttp * h, char * (f)(int *))
|
sendXMLdesc(struct upnphttp * h, char * (f)(int *))
|
||||||
@ -551,17 +532,31 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
|||||||
HttpVer[i] = '\0';
|
HttpVer[i] = '\0';
|
||||||
syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
|
syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
|
||||||
HttpCommand, HttpUrl, HttpVer);
|
HttpCommand, HttpUrl, HttpVer);
|
||||||
//DEBUG printf("HTTP REQUEST:\n%s\n", h->req_buf);
|
//DEBUG printf("HTTP REQUEST:\n%.*s\n", h->req_buflen, h->req_buf);
|
||||||
ParseHttpHeaders(h);
|
ParseHttpHeaders(h);
|
||||||
|
|
||||||
if( (h->reqflags & FLAG_CHUNKED) && (h->req_chunklen > (h->req_buflen - h->req_contentoff) || h->req_chunklen < 0) )
|
/* see if we need to wait for remaining data */
|
||||||
|
if( (h->reqflags & FLAG_CHUNKED) )
|
||||||
{
|
{
|
||||||
/* waiting for remaining data */
|
char * chunkstart = h->req_buf+h->req_contentoff;
|
||||||
printf("*** %d < %d\n", (h->req_buflen - h->req_contentoff), h->req_contentlen);
|
char * numstart;
|
||||||
printf("Chunked request [%ld]. Need more input.\n", h->req_chunklen);
|
|
||||||
h->state = 2;
|
h->state = 2;
|
||||||
|
while( h->req_chunklen )
|
||||||
|
{
|
||||||
|
if( chunkstart >= (h->req_buf+h->req_buflen) )
|
||||||
|
return;
|
||||||
|
numstart = chunkstart+h->req_chunklen+2;
|
||||||
|
h->req_chunklen = strtol(numstart, &chunkstart, 16);
|
||||||
|
if( !h->req_chunklen && (chunkstart == numstart) )
|
||||||
|
{
|
||||||
|
printf("Chunked request needs more input.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chunkstart = chunkstart+2;
|
||||||
|
}
|
||||||
|
h->state = 100;
|
||||||
}
|
}
|
||||||
else if(strcmp("POST", HttpCommand) == 0)
|
if(strcmp("POST", HttpCommand) == 0)
|
||||||
{
|
{
|
||||||
h->req_command = EPost;
|
h->req_command = EPost;
|
||||||
ProcessHTTPPOST_upnphttp(h);
|
ProcessHTTPPOST_upnphttp(h);
|
||||||
@ -602,12 +597,6 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
|||||||
{
|
{
|
||||||
sendXMLdesc(h, genX_MS_MediaReceiverRegistrar);
|
sendXMLdesc(h, genX_MS_MediaReceiverRegistrar);
|
||||||
}
|
}
|
||||||
#ifdef HAS_DUMMY_SERVICE
|
|
||||||
else if(strcmp(DUMMY_PATH, HttpUrl) == 0)
|
|
||||||
{
|
|
||||||
sendDummyDesc(h);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else if(strncmp(HttpUrl, "/MediaItems/", 12) == 0)
|
else if(strncmp(HttpUrl, "/MediaItems/", 12) == 0)
|
||||||
{
|
{
|
||||||
SendResp_dlnafile(h, HttpUrl+12);
|
SendResp_dlnafile(h, HttpUrl+12);
|
||||||
@ -864,6 +853,7 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
|
|||||||
char header[1500];
|
char header[1500];
|
||||||
char sql_buf[256];
|
char sql_buf[256];
|
||||||
char **result;
|
char **result;
|
||||||
|
int rows;
|
||||||
char date[30];
|
char date[30];
|
||||||
time_t curtime = time(NULL);
|
time_t curtime = time(NULL);
|
||||||
int n;
|
int n;
|
||||||
@ -879,8 +869,14 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(sql_buf, "SELECT PATH from OBJECTS where OBJECT_ID = '%s'", object);
|
sprintf(sql_buf, "SELECT d.PATH from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
||||||
sqlite3_get_table(db, sql_buf, &result, 0, 0, 0);
|
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
|
||||||
|
if( !rows )
|
||||||
|
{
|
||||||
|
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", object);
|
||||||
|
Send404(h);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
printf("Serving up thumbnail for ObjectId: %s [%s]\n", object, result[1]);
|
printf("Serving up thumbnail for ObjectId: %s [%s]\n", object, result[1]);
|
||||||
|
|
||||||
if( access(result[1], F_OK) == 0 )
|
if( access(result[1], F_OK) == 0 )
|
||||||
@ -976,7 +972,7 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(sql_buf, "SELECT o.PATH, d.WIDTH, d.HEIGHT from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
sprintf(sql_buf, "SELECT d.PATH, d.WIDTH, d.HEIGHT from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
||||||
sqlite3_get_table(db, sql_buf, &result, 0, 0, 0);
|
sqlite3_get_table(db, sql_buf, &result, 0, 0, 0);
|
||||||
printf("Serving up resized image for ObjectId: %s [%s]\n", object, result[1]);
|
printf("Serving up resized image for ObjectId: %s [%s]\n", object, result[1]);
|
||||||
|
|
||||||
@ -1073,17 +1069,26 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
|||||||
time_t curtime = time(NULL);
|
time_t curtime = time(NULL);
|
||||||
off_t total, send_size;
|
off_t total, send_size;
|
||||||
char *path, *mime, *dlna;
|
char *path, *mime, *dlna;
|
||||||
|
#if USE_FORK
|
||||||
|
pid_t newpid = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
memset(header, 0, 1500);
|
memset(header, 0, 1500);
|
||||||
|
|
||||||
sprintf(sql_buf, "SELECT o.PATH, d.MIME, d.DLNA_PN from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
sprintf(sql_buf, "SELECT d.PATH, d.MIME, d.DLNA_PN from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
||||||
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
|
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
|
||||||
if( !rows )
|
if( !rows )
|
||||||
{
|
{
|
||||||
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", object);
|
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", object);
|
||||||
Send404(h);
|
Send404(h);
|
||||||
goto error;
|
sqlite3_free_table(result);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
#if USE_FORK
|
||||||
|
newpid = fork();
|
||||||
|
if( newpid )
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
path = result[3];
|
path = result[3];
|
||||||
mime = result[4];
|
mime = result[4];
|
||||||
@ -1186,8 +1191,8 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
|||||||
}
|
}
|
||||||
else //if( h->reqflags & FLAG_XFERINTERACTIVE )
|
else //if( h->reqflags & FLAG_XFERINTERACTIVE )
|
||||||
{
|
{
|
||||||
if( (strncmp(mime, "vide", 4) == 0) ||
|
if( (strncmp(mime, "video", 5) == 0) ||
|
||||||
(strncmp(mime, "audi", 4) == 0) )
|
(strncmp(mime, "audio", 5) == 0) )
|
||||||
strcat(header, "transferMode.dlna.org: Streaming\r\n");
|
strcat(header, "transferMode.dlna.org: Streaming\r\n");
|
||||||
else
|
else
|
||||||
strcat(header, "transferMode.dlna.org: Interactive\r\n");
|
strcat(header, "transferMode.dlna.org: Interactive\r\n");
|
||||||
@ -1240,4 +1245,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
|||||||
}
|
}
|
||||||
error:
|
error:
|
||||||
sqlite3_free_table(result);
|
sqlite3_free_table(result);
|
||||||
|
#if USE_FORK
|
||||||
|
_exit(0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
101
upnpsoap.c
101
upnpsoap.c
@ -64,28 +64,6 @@ BuildSendAndCloseSoapResp(struct upnphttp * h,
|
|||||||
CloseSocket_upnphttp(h);
|
CloseSocket_upnphttp(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
GetStatusInfo(struct upnphttp * h, const char * action)
|
|
||||||
{
|
|
||||||
static const char resp[] =
|
|
||||||
"<u:%sResponse "
|
|
||||||
"xmlns:u=\"%s\">"
|
|
||||||
"<NewConnectionStatus>Connected</NewConnectionStatus>"
|
|
||||||
"<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>"
|
|
||||||
"<NewUptime>%ld</NewUptime>"
|
|
||||||
"</u:%sResponse>";
|
|
||||||
|
|
||||||
char body[512];
|
|
||||||
int bodylen;
|
|
||||||
time_t uptime;
|
|
||||||
|
|
||||||
uptime = (time(NULL) - startup_time);
|
|
||||||
bodylen = snprintf(body, sizeof(body), resp,
|
|
||||||
action, "urn:schemas-upnp-org:service:WANIPConnection:1",
|
|
||||||
(long)uptime, action);
|
|
||||||
BuildSendAndCloseSoapResp(h, body, bodylen);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
GetSystemUpdateID(struct upnphttp * h, const char * action)
|
GetSystemUpdateID(struct upnphttp * h, const char * action)
|
||||||
{
|
{
|
||||||
@ -129,16 +107,15 @@ GetProtocolInfo(struct upnphttp * h, const char * action)
|
|||||||
"<u:%sResponse "
|
"<u:%sResponse "
|
||||||
"xmlns:u=\"%s\">"
|
"xmlns:u=\"%s\">"
|
||||||
"<Source>"
|
"<Source>"
|
||||||
/*"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=01;DLNA.ORG_CI=1,"
|
|
||||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01,"
|
|
||||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01,"
|
|
||||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01,"
|
|
||||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01,"*/
|
|
||||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,"
|
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,"
|
||||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01,"
|
"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,"
|
"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,"
|
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC,"
|
"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/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:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||||
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
|
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
|
||||||
"http-get:*:audio/x-ms-wma:*,"
|
"http-get:*:audio/x-ms-wma:*,"
|
||||||
"http-get:*:audio/wav:*,"
|
"http-get:*:audio/wav:*,"
|
||||||
@ -212,7 +189,7 @@ GetCurrentConnectionIDs(struct upnphttp * h, const char * action)
|
|||||||
static const char resp[] =
|
static const char resp[] =
|
||||||
"<u:%sResponse "
|
"<u:%sResponse "
|
||||||
"xmlns:u=\"%s\">"
|
"xmlns:u=\"%s\">"
|
||||||
"<ConnectionIDs>-1</ConnectionIDs>"
|
"<ConnectionIDs>0</ConnectionIDs>"
|
||||||
"</u:%sResponse>";
|
"</u:%sResponse>";
|
||||||
|
|
||||||
char body[512];
|
char body[512];
|
||||||
@ -378,13 +355,16 @@ static int callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
sprintf(str_buf, "childCount=\"%s\"", result[1]);
|
sprintf(str_buf, "childCount=\"%s\"", result[1]);
|
||||||
strcat(passed_args->resp, str_buf);
|
strcat(passed_args->resp, str_buf);
|
||||||
}
|
}
|
||||||
/* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s */
|
/* 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) )
|
||||||
{
|
{
|
||||||
strcat(passed_args->resp, ">"
|
if( !passed_args->filter || strstr(passed_args->filter, "upnp:searchClass") )
|
||||||
"<upnp:searchClass includeDerived=\"1\">object.item.audioItem</upnp:searchClass>"
|
{
|
||||||
"<upnp:searchClass includeDerived=\"1\">object.item.imageItem</upnp:searchClass>"
|
strcat(passed_args->resp, ">"
|
||||||
"<upnp:searchClass includeDerived=\"1\">object.item.videoItem</upnp:searchClass");
|
"<upnp:searchClass includeDerived=\"1\">object.item.audioItem</upnp:searchClass>"
|
||||||
|
"<upnp:searchClass includeDerived=\"1\">object.item.imageItem</upnp:searchClass>"
|
||||||
|
"<upnp:searchClass includeDerived=\"1\">object.item.videoItem</upnp:searchClass");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sprintf(str_buf, ">"
|
sprintf(str_buf, ">"
|
||||||
"<dc:title>%s</dc:title>"
|
"<dc:title>%s</dc:title>"
|
||||||
@ -575,49 +555,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
|||||||
free(resp);
|
free(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
GetExternalIPAddress(struct upnphttp * h, const char * action)
|
|
||||||
{
|
|
||||||
static const char resp[] =
|
|
||||||
"<u:%sResponse "
|
|
||||||
"xmlns:u=\"%s\">"
|
|
||||||
"<NewExternalIPAddress>%s</NewExternalIPAddress>"
|
|
||||||
"</u:%sResponse>";
|
|
||||||
|
|
||||||
char body[512];
|
|
||||||
int bodylen;
|
|
||||||
char ext_ip_addr[INET_ADDRSTRLEN];
|
|
||||||
|
|
||||||
#ifndef MULTIPLE_EXTERNAL_IP
|
|
||||||
if(use_ext_ip_addr)
|
|
||||||
{
|
|
||||||
strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN);
|
|
||||||
}
|
|
||||||
else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0)
|
|
||||||
{
|
|
||||||
syslog(LOG_ERR, "Failed to get ip address for interface %s",
|
|
||||||
ext_if_name);
|
|
||||||
strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int i;
|
|
||||||
strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
|
|
||||||
for(i = 0; i<n_lan_addr; i++)
|
|
||||||
{
|
|
||||||
if( (h->clientaddr.s_addr & lan_addr[i].mask.s_addr)
|
|
||||||
== (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
|
|
||||||
{
|
|
||||||
strncpy(ext_ip_addr, lan_addr[i].ext_ip_str, INET_ADDRSTRLEN);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
bodylen = snprintf(body, sizeof(body), resp,
|
|
||||||
action, "urn:schemas-upnp-org:service:WANIPConnection:1",
|
|
||||||
ext_ip_addr, action);
|
|
||||||
BuildSendAndCloseSoapResp(h, body, bodylen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If a control point calls QueryStateVariable on a state variable that is not
|
If a control point calls QueryStateVariable on a state variable that is not
|
||||||
buffered in memory within (or otherwise available from) the service,
|
buffered in memory within (or otherwise available from) the service,
|
||||||
@ -659,7 +596,7 @@ QueryStateVariable(struct upnphttp * h, const char * action)
|
|||||||
BuildSendAndCloseSoapResp(h, body, bodylen);
|
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
/* not usefull */
|
/* not useful */
|
||||||
else if(strcmp(var_name, "ConnectionType") == 0)
|
else if(strcmp(var_name, "ConnectionType") == 0)
|
||||||
{
|
{
|
||||||
bodylen = snprintf(body, sizeof(body), resp, "IP_Routed");
|
bodylen = snprintf(body, sizeof(body), resp, "IP_Routed");
|
||||||
@ -687,9 +624,7 @@ static const struct
|
|||||||
}
|
}
|
||||||
soapMethods[] =
|
soapMethods[] =
|
||||||
{
|
{
|
||||||
{ "GetExternalIPAddress", GetExternalIPAddress},
|
|
||||||
{ "QueryStateVariable", QueryStateVariable},
|
{ "QueryStateVariable", QueryStateVariable},
|
||||||
{ "GetStatusInfo", GetStatusInfo},
|
|
||||||
{ "Browse", BrowseContentDirectory},
|
{ "Browse", BrowseContentDirectory},
|
||||||
{ "Search", SearchContentDirectory},
|
{ "Search", SearchContentDirectory},
|
||||||
{ "GetSearchCapabilities", GetSearchCapabilities},
|
{ "GetSearchCapabilities", GetSearchCapabilities},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user