integrate server-side conversion patches, -Wall cleanups, AMD64 fixes, and xml-rpc cleanups

This commit is contained in:
Ron Pedde 2005-02-21 08:10:37 +00:00
parent c33eade8ba
commit bdd5ba39da
13 changed files with 349 additions and 20 deletions

View File

@ -68,3 +68,11 @@ Roger Mundt
dirkthedaring2 (?)
* fixes for inverted playlist
* speedups on connect
Adrian Schroeter
* fixes for AMD64
* -Wall cleanups
Timo J. Rinne
* Server-side format conversion (on-the-fly transcoding)

View File

@ -122,7 +122,37 @@ playlist /etc/mt-daapd.playlist
#
#
extensions .mp3,.m4a,.m4p
extensions .mp3,.m4a,.m4p,.ogg
#
# ssc_extensions (optional)
#
# List of file extensions belonging to the files daap server
# performs internal format conversion and present to clients
# as WAV files. Extensions must also be present in 'extensions'
# configuration value, or files are not probed in the first
# place.
#
ssc_extensions .ogg
#
# ssc_prog (optional)
#
# Program that is used in server side format conversion.
# Program must accept following command line syntax:
# ssc_prog filename offset
# Parameter filename is the real name of the file that is
# to be converted and streamed, offset is number of bytes
# that are skipped from the beginning of the _output_ file
# before streaming is started. The resulting wav file (or
# rest of the file after initial seek) is written to the
# standard output by the ssc_prog program. This is typically
# a script that is a front end for different conversion tools
# handling different formats.
#
ssc_prog /etc/mt-daapd-ssc-script
#
# logfile (optional)

View File

@ -28,7 +28,7 @@ mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
mp3-scanner.h mp3-scanner.c playlist.c playlist.h \
rend-unix.h lexer.l parser.y strcasestr.c strcasestr.h strsep.c \
redblack.c redblack.h dynamic-art.c dynamic-art.h query.c query.h \
xml-rpc.h xml-rpc.c \
xml-rpc.h xml-rpc.c ssc.c ssc.h \
$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC)
EXTRA_DIST = mDNS.c mDNSClientAPI.h mDNSDebug.h mDNSPosix.c \

View File

@ -117,6 +117,8 @@ CONFIGELEMENT config_elements[] = {
{ 1,0,0,CONFIG_TYPE_INT,"compress",(void*)&config.compress,config_emit_int },
{ 1,0,0,CONFIG_TYPE_STRING,"playlist",(void*)&config.playlist,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"extensions",(void*)&config.extensions,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"ssc_extensions",(void*)&config.ssc_extensions,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"ssc_prog",(void*)&config.ssc_prog,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"password",(void*)&config.readpassword, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"logfile",(void*)&config.logfile, config_emit_string },
{ 0,0,0,CONFIG_TYPE_SPECIAL,"release",(void*)VERSION,config_emit_literal },
@ -275,6 +277,8 @@ int config_read(char *file) {
/* DWB: use alloced space so it can be freed without errors */
config.extensions=strdup(".mp3");
config.ssc_extensions=strdup("");
config.ssc_prog=strdup("");
/* DWB: use alloced space so it can be freed without errors */
config.servername=strdup("mt-daapd " VERSION);
@ -479,6 +483,8 @@ int config_write(WS_CONNINFO *pwsc) {
if(ws_getvar(pwsc,"password") && strlen(ws_getvar(pwsc,"password")))
fprintf(configfile,"password\t%s\n",ws_getvar(pwsc,"password"));
fprintf(configfile,"extensions\t%s\n",ws_getvar(pwsc,"extensions"));
fprintf(configfile,"ssc_extensions\t%s\n",ws_getvar(pwsc,"ssc_extensions"));
fprintf(configfile,"ssc_prog\t%s\n",ws_getvar(pwsc,"ssc_prog"));
fprintf(configfile,"db_dir\t\t%s\n",ws_getvar(pwsc,"db_dir"));
fprintf(configfile,"rescan_interval\t%s\n",ws_getvar(pwsc,"rescan_interval"));
fprintf(configfile,"scan_type\t%s\n",ws_getvar(pwsc,"scan_type"));

View File

@ -64,6 +64,8 @@ typedef struct tag_config {
char *runas; /**< Who to drop privs to (if run as root) */
char *dbdir; /**< Where to put the db file */
char *extensions; /**< What music file extentions to process */
char *ssc_extensions; /**< What extensions are converted in server */
char *ssc_prog; /**< Server side music format converter prog */
char *artfilename; /**< What filename to merge coverart with */
char *logfile; /**< What file to use as a logfile */
STATS stats; /**< Stats structure (see above) */

View File

@ -23,6 +23,9 @@
Change History (most recent first):
$Log$
Revision 1.5 2005/02/21 08:10:34 rpedde
integrate server-side conversion patches, -Wall cleanups, AMD64 fixes, and xml-rpc cleanups
Revision 1.4 2005/01/10 01:07:01 rpedde
Synchronize mDNS to Apples 58.8 drop
@ -523,13 +526,9 @@ typedef signed char mDNSs8;
typedef unsigned char mDNSu8;
typedef signed short mDNSs16;
typedef unsigned short mDNSu16;
#if _LP64
typedef signed int mDNSs32;
typedef unsigned int mDNSu32;
#else
typedef signed long mDNSs32;
typedef unsigned long mDNSu32;
#endif
#include <sys/types.h>
typedef int32_t mDNSs32;
typedef u_int32_t mDNSu32;
// To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct
// This way, mDNSInterfaceIDs can be assigned, and compared with each other, but not with other types

View File

@ -23,6 +23,9 @@
Change History (most recent first):
$Log$
Revision 1.3 2005/02/21 08:10:34 rpedde
integrate server-side conversion patches, -Wall cleanups, AMD64 fixes, and xml-rpc cleanups
Revision 1.2 2005/01/10 01:07:01 rpedde
Synchronize mDNS to Apples 58.8 drop
@ -151,8 +154,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases)
for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
ifr = (struct ifreq *) ptr;
len = GET_SA_LEN(ifr->ifr_addr);
ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */
ptr += sizeof(*ifr);
// fprintf(stderr, "intf %d name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family);

View File

@ -78,6 +78,7 @@
#include "mp3-scanner.h"
#include "webserver.h"
#include "playlist.h"
#include "ssc.h"
#include "dynamic-art.h"
#ifndef WITHOUT_MDNS
@ -184,6 +185,7 @@ void daap_handler(WS_CONNINFO *pwsc) {
MP3FILE *pmp3;
int file_fd;
FILE *file_ptr; /* for possible conv filter */
int session_id=0;
int img_fd;
@ -194,6 +196,8 @@ void daap_handler(WS_CONNINFO *pwsc) {
off_t real_len;
off_t file_len;
char *real_path;
int bytes_copied=0;
GZIP_STREAM *gz;
@ -384,6 +388,77 @@ void daap_handler(WS_CONNINFO *pwsc) {
if(!pmp3) {
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
ws_returnerror(pwsc,404,"File Not Found");
} else if ((real_path=server_side_convert_path(pmp3->path)) != NULL) {
// The file should be converted in the server side.
DPRINTF(E_WARN,L_WS,"Thread %d: Autoconvert file %s for client\n",
pwsc->threadno,real_path);
file_ptr=server_side_convert_open(real_path,offset);
if (file_ptr) {
file_fd = fileno(file_ptr);
} else {
file_fd = -1;
}
if(file_fd == -1) {
if (file_ptr) {
server_side_convert_close(file_ptr);
}
pwsc->error=errno;
DPRINTF(E_WARN,L_WS,
"Thread %d: Error opening %s for conversion\n",
pwsc->threadno,real_path);
ws_returnerror(pwsc,404,"Not found");
config_set_status(pwsc,session_id,NULL);
db_dispose(pmp3);
free(pmp3);
free(real_path);
} else {
// DWB: fix content-type to correctly reflect data
// content type (dmap tagged) should only be used on
// dmap protocol requests, not the actually song data
if(pmp3->type)
ws_addresponseheader(pwsc,"Content-Type","audio/%s",
pmp3->type);
// Also content-length -heade would be nice, but since
// we don't really know it here, so let's leave it out.
ws_addresponseheader(pwsc,"Connection","Close");
if(!offset)
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
else {
// This is actually against the protocol, since
// range MUST be explicit according to HTTP-standard
// Seems to work at least with iTunes.
ws_addresponseheader(pwsc,
"Content-Range","bytes %ld-*/*",
(long)offset);
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
}
ws_emitheaders(pwsc);
config_set_status(pwsc,session_id,
"Streaming file via convert filter '%s'",
pmp3->fname);
DPRINTF(E_LOG,L_WS,
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
session_id,pmp3->fname, pwsc->hostname,(long)offset);
if(!offset)
config.stats.songs_served++; /* FIXME: remove stat races */
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
DPRINTF(E_INF,L_WS,
"Error copying converted file to remote... %s\n",
strerror(errno));
} else {
DPRINTF(E_INF,L_WS,
"Finished streaming converted file to remote\n");
}
server_side_convert_close(file_ptr);
config_set_status(pwsc,session_id,NULL);
db_dispose(pmp3);
free(pmp3);
free(real_path);
}
} else {
/* got the file, let's open and serve it */
file_fd=r_open2(pmp3->path,O_RDONLY);
@ -634,8 +709,8 @@ void *signal_handler(void *arg) {
switch(sig) {
case SIGCLD:
DPRINTF(E_LOG,L_MAIN,"Got CLD signal. Reaping\n");
while (wait(&status)) {
};
while (wait3(&status, WNOHANG, NULL) > 0) {
}
break;
case SIGINT:
DPRINTF(E_LOG,L_MAIN,"Got INT signal. Notifying daap server.\n");

View File

@ -50,6 +50,7 @@
#include "err.h"
#include "mp3-scanner.h"
#include "playlist.h"
#include "ssc.h"
#ifndef HAVE_STRCASESTR
# include "strcasestr.h"
@ -384,6 +385,7 @@ int scan_path(char *path) {
char mp3_path[PATH_MAX];
struct stat sb;
int modified_time;
char *ext;
if((current_dir=opendir(path)) == NULL) {
DPRINTF(E_WARN,L_SCAN,"opendir: %s\n",strerror(errno));
@ -429,9 +431,8 @@ int scan_path(char *path) {
config.process_m3u){
/* we found an m3u file */
scan_static_playlist(path, pde, &sb);
} else if (strcasestr(config.extensions,
(char*)&pde->d_name[strlen(pde->d_name) - 4])) {
} else if (((ext = strrchr(pde->d_name, '.')) != NULL) &&
(strcasestr(config.extensions, ext))) {
/* only scan if it's been changed, or empty db */
modified_time=sb.st_mtime;
DPRINTF(E_DBG,L_SCAN,"FS Mod time: %d\n",modified_time);
@ -533,8 +534,8 @@ void scan_music_file(char *path, struct dirent *pde, struct stat *psb) {
DPRINTF(E_INF,L_SCAN,"Found music file: %s\n",pde->d_name);
memset((void*)&mp3file,0,sizeof(mp3file));
mp3file.path=mp3_path;
mp3file.fname=pde->d_name;
mp3file.path=strdup(mp3_path);
mp3file.fname=strdup(pde->d_name);
if(strlen(pde->d_name) > 4)
mp3file.type=strdup(strrchr(pde->d_name, '.') + 1);
@ -557,6 +558,8 @@ void scan_music_file(char *path, struct dirent *pde, struct stat *psb) {
mp3file.time_added=psb->st_ctime;
mp3file.time_modified=psb->st_mtime;
server_side_convert_set(&mp3file);
DPRINTF(E_DBG,L_SCAN," Date Added: %d\n",mp3file.time_added);
db_add(&mp3file);
@ -942,6 +945,8 @@ int scan_get_mp3tags(char *file, MP3FILE *pmp3) {
* Free up the tags that were dynamically allocated
*/
int scan_freetags(MP3FILE *pmp3) {
MAYBEFREE(pmp3->path);
MAYBEFREE(pmp3->fname);
MAYBEFREE(pmp3->title);
MAYBEFREE(pmp3->artist);
MAYBEFREE(pmp3->album);
@ -1769,3 +1774,4 @@ void make_composite_tags(MP3FILE *song)
/* Ogg used to be set as an item_kind of 4. Dunno why */
song->item_kind = 2;
}

View File

@ -422,6 +422,8 @@ int query_test(query_node_t* query, void* target)
case qot_const:
return query->left.constant;
}
/* should not happen */
return 0;
}
void query_free(query_node_t* query)

162
src/ssc.c Normal file
View File

@ -0,0 +1,162 @@
/*
* $Id$
* Implementation file for server side format conversion.
*
* Copyright (C) 2005 Timo J. Rinne (tri@iki.fi)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#define _POSIX_PTHREAD_SEMANTICS
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <id3tag.h>
#include <limits.h>
#include <restart.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h> /* htons and friends */
#include <sys/stat.h>
#include <dirent.h> /* why here? For osx 10.2, of course! */
#include "daapd.h"
#include "db-memory.h"
#include "err.h"
#include "mp3-scanner.h"
#include "ssc.h"
#ifndef HAVE_STRCASESTR
# include "strcasestr.h"
#endif
/**
* If path is server side convertable, return the path to real file.
*
* @param path char * to the path in the database.
*/
char *server_side_convert_path(char *path)
{
char *r = NULL;
if (path &&
(strlen(path) > strlen(SERVER_SIDE_CONVERT_SUFFIX)) &&
(strcmp(path + strlen(path) - strlen(SERVER_SIDE_CONVERT_SUFFIX),
SERVER_SIDE_CONVERT_SUFFIX) == 0)) {
/* Lose the artificial suffix. Could use strndup here.*/
r = strdup(path);
r[strlen(path) - strlen(SERVER_SIDE_CONVERT_SUFFIX)] = '\0';
}
return r;
}
/**
* Check if the file entry (otherwise complete) is such that
* file should be converted in server end to wav-format.
* If so, the info is modified accordingly and non-zero return
* value is returned.
*
* @param song MP3FILE of the file to possibly set to server side conversion
*/
int server_side_convert_set(MP3FILE *pmp3)
{
char *fname, *path, *description, *ext;
if ((!config.ssc_extensions) ||
(!config.ssc_extensions[0]) ||
(!config.ssc_prog) ||
(!config.ssc_prog[0]) ||
(!pmp3->fname) ||
(!pmp3->path) ||
(!pmp3->type) ||
((strlen(pmp3->fname) > strlen(SERVER_SIDE_CONVERT_SUFFIX)) &&
(strcmp(pmp3->fname +
strlen(pmp3->fname) -
strlen(SERVER_SIDE_CONVERT_SUFFIX),
SERVER_SIDE_CONVERT_SUFFIX) == 0))) {
return 0;
}
if (((ext = strrchr(pmp3->path, '.')) != NULL) &&
(strcasestr(config.ssc_extensions, ext))) {
fname = (char *)malloc(strlen(pmp3->fname) +
strlen(SERVER_SIDE_CONVERT_SUFFIX) + 1);
path = (char *)malloc(strlen(pmp3->path) +
strlen(SERVER_SIDE_CONVERT_SUFFIX) + 1);
description = (char *)malloc(strlen(pmp3->description) +
strlen(SERVER_SIDE_CONVERT_DESCR) + 1);
strcpy(fname, pmp3->fname);
strcat(fname, SERVER_SIDE_CONVERT_SUFFIX);
free(pmp3->fname);
pmp3->fname = fname;
strcpy(path, pmp3->path);
strcat(path, SERVER_SIDE_CONVERT_SUFFIX);
free(pmp3->path);
pmp3->path = path;
strcpy(description, pmp3->description);
strcat(description, SERVER_SIDE_CONVERT_DESCR);
free(pmp3->description);
pmp3->description = description;
free(pmp3->type);
pmp3->type = strdup("wav");
if (pmp3->samplerate > 0) {
// Here we guess that it's always 16 bit stereo samples,
// which is accurate enough for now.
pmp3->bitrate = (pmp3->samplerate * 4 * 8) / 1000;
}
return 1;
}
return 0;
}
/**
* Open the source file with convert fiter.
*
* @param path char * to the real filename.
* @param offset off_t to the point in file where the streaming starts.
*/
FILE *server_side_convert_open(char *path, off_t offset)
{
char *cmd;
FILE *f;
cmd=(char *)malloc(strlen(config.ssc_prog) +
strlen(path) +
64);
sprintf(cmd, "%s \"%s\" %ld",
config.ssc_prog, path, (long)offset);
f = popen(cmd, "r");
return f;
}
/**
* Open the source file with convert fiter.
*
* @param FILE * returned by server_side_convert_open be closed.
*/
void server_side_convert_close(FILE *f)
{
if (f)
pclose(f);
return;
}

34
src/ssc.h Normal file
View File

@ -0,0 +1,34 @@
/*
* $Id$
* Implementation file for server side format conversion.
*
* Copyright (C) 2005 Timo J. Rinne (tri@iki.fi)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SCC_H_
#define _SCC_H_
#define SERVER_SIDE_CONVERT_SUFFIX ".-*-ssc-*-.wav"
#define SERVER_SIDE_CONVERT_DESCR " (converted to WAV)"
extern int server_side_convert_set(MP3FILE *pmp3);
extern char *server_side_convert_path(char *path);
extern FILE *server_side_convert_open(char *path, off_t offset);
extern void server_side_convert_close(FILE *f);
#endif /* _SCC_H_ */

View File

@ -54,7 +54,7 @@ void xml_get_playlists(WS_CONNINFO *pwsc) {
ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
ws_emitheaders(pwsc);
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
ws_writefd(pwsc,"<playlists>");
/* enumerate all the playlists */
@ -99,15 +99,18 @@ void xml_get_playlistitems(WS_CONNINFO *pwsc) {
playlistid=atoi(playlistnum);
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
ws_writefd(pwsc,"<playlist>");
henum=db_playlist_items_enum_begin(playlistid);
while((itemid=db_playlist_items_enum(&henum)) != -1) {
current=db_find(itemid);
if(0 != current) {
ws_writefd(pwsc,"<item>%lu</item>",itemid);
ws_writefd(pwsc,"<item>");
ws_writefd(pwsc,"<id>%lu</id>",itemid);
temp=xml_entity_encode(current->title);
ws_writefd(pwsc,"<name>%s</name>",temp);
ws_writefd(pwsc,"</item>");
free(temp);
db_dispose(current);
free(current);