add ffmpeg-based transcoding
This commit is contained in:
parent
da8f1a6f8f
commit
0f64686356
|
@ -3,7 +3,7 @@
|
|||
SUBDIRS=plugins
|
||||
|
||||
sbin_PROGRAMS = mt-daapd
|
||||
bin_PROGRAMS = wavstreamer mtd-update
|
||||
bin_PROGRAMS = wavstreamer
|
||||
|
||||
if COND_REND_POSIX
|
||||
PRENDSRC=mDNS.c mDNSClientAPI.h mDNSDebug.h mDNSPosix.c mDNSUNP.c \
|
||||
|
@ -46,20 +46,12 @@ if COND_GDBM
|
|||
GDBM=db-gdbm.c db-gdbm.h
|
||||
endif
|
||||
|
||||
mtd_update_SOURCES = mtd-update.c conf.c conf.h ll.c ll.h \
|
||||
db-sql.c db-sql.h db-generic.c db-generic.h smart-parser.c \
|
||||
smart-parser.h err.c err.h os-unix.c os.h xml-rpc.c xml-rpc.h \
|
||||
restart.c restart.h uici.c uici.h ssc.c ssc.h \
|
||||
webserver.c webserver.h compat.c compat.h \
|
||||
$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) \
|
||||
$(SQLITEDB) $(SQLITE3DB)
|
||||
|
||||
wavstreamer_SOURCES = wavstreamer.c
|
||||
|
||||
mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
|
||||
webserver.h configfile.c configfile.h err.c err.h restart.c restart.h \
|
||||
mp3-scanner.h mp3-scanner.c rend-unix.h \
|
||||
dynamic-art.c dynamic-art.h ssc.c ssc.h \
|
||||
dynamic-art.c dynamic-art.h \
|
||||
db-generic.c db-generic.h dispatch.c dispatch.h \
|
||||
rxml.c rxml.h redblack.c redblack.h scan-mp3.c scan-mp4.c \
|
||||
scan-xml.c scan-wma.c scan-aac.c scan-aac.h scan-wav.c scan-url.c \
|
||||
|
|
|
@ -39,8 +39,8 @@
|
|||
#include "db-generic.h"
|
||||
#include "db-sql.h"
|
||||
#include "restart.h"
|
||||
#include "ssc.h"
|
||||
#include "smart-parser.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#ifdef HAVE_LIBSQLITE
|
||||
#include "db-sql-sqlite2.h"
|
||||
|
@ -1354,7 +1354,7 @@ int db_sql_get_size(DBQUERYINFO *pinfo, SQL_ROW valarray) {
|
|||
case queryTypeItems:
|
||||
case queryTypePlaylistItems: /* essentially the same query */
|
||||
/* see if this is going to be transcoded */
|
||||
transcode = server_side_convert(valarray[37]);
|
||||
transcode = plugin_ssc_can_transcode(valarray[37]);
|
||||
|
||||
/* Items that get changed by transcode:
|
||||
*
|
||||
|
@ -1530,7 +1530,7 @@ int db_sql_build_dmap(DBQUERYINFO *pinfo, char **valarray, unsigned char *presul
|
|||
case queryTypeItems:
|
||||
case queryTypePlaylistItems: /* essentially the same query */
|
||||
/* see if this is going to be transcoded */
|
||||
transcode = server_side_convert(valarray[37]);
|
||||
transcode = plugin_ssc_can_transcode(valarray[37]);
|
||||
|
||||
/* Items that get changed by transcode:
|
||||
*
|
||||
|
|
|
@ -42,8 +42,8 @@
|
|||
#include "configfile.h"
|
||||
#include "err.h"
|
||||
#include "mp3-scanner.h"
|
||||
#include "plugin.h"
|
||||
#include "webserver.h"
|
||||
#include "ssc.h"
|
||||
#include "dynamic-art.h"
|
||||
#include "restart.h"
|
||||
#include "daapd.h"
|
||||
|
@ -739,7 +739,6 @@ char *dispatch_xml_encode(char *original, int len) {
|
|||
|
||||
void dispatch_stream_id(WS_CONNINFO *pwsc, int session, char *id) {
|
||||
MP3FILE *pmp3;
|
||||
FILE *file_ptr;
|
||||
int file_fd;
|
||||
int bytes_copied;
|
||||
off_t real_len;
|
||||
|
@ -765,84 +764,23 @@ void dispatch_stream_id(WS_CONNINFO *pwsc, int session, char *id) {
|
|||
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
|
||||
config_set_status(pwsc,session,NULL);
|
||||
ws_returnerror(pwsc,404,"File Not Found");
|
||||
} else if (server_side_convert(pmp3->codectype)) {
|
||||
} else if (plugin_ssc_can_transcode(pmp3->codectype)) {
|
||||
/************************
|
||||
* Server side conversion
|
||||
************************/
|
||||
DPRINTF(E_WARN,L_WS,"Thread %d: Autoconvert file %s for client\n",
|
||||
pwsc->threadno,pmp3->path);
|
||||
file_ptr = server_side_convert_open(pmp3->path,
|
||||
offset,
|
||||
pmp3->song_length,
|
||||
pmp3->codectype);
|
||||
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,pmp3->path);
|
||||
ws_returnerror(pwsc,404,"Not found");
|
||||
db_dispose_item(pmp3);
|
||||
} else {
|
||||
// The type should really be determined by the transcoding
|
||||
// function -- it's possible that you want to transcode
|
||||
// to a lower-bitrate mp3 or something... but for now,
|
||||
// we'll just assume .wav
|
||||
ws_addresponseheader(pwsc,"Content-Type","audio/wav");
|
||||
config_set_status(pwsc,session,
|
||||
"Transcoding '%s' (id %d)",
|
||||
pmp3->title,pmp3->id);
|
||||
|
||||
/*
|
||||
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");
|
||||
DPRINTF(E_LOG,L_WS,
|
||||
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
|
||||
session,pmp3->fname, pwsc->hostname,(long)offset);
|
||||
|
||||
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");
|
||||
}
|
||||
bytes_copied = plugin_ssc_transcode(pwsc,pmp3->path,pmp3->codectype,
|
||||
pmp3->song_length,offset);
|
||||
|
||||
ws_emitheaders(pwsc);
|
||||
|
||||
config_set_status(pwsc,session,
|
||||
"Transcoding '%s' (id %d)",
|
||||
pmp3->title,pmp3->id);
|
||||
DPRINTF(E_LOG,L_WS,
|
||||
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
|
||||
session,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");
|
||||
db_playcount_increment(NULL,pmp3->id);
|
||||
|
||||
}
|
||||
server_side_convert_close(file_ptr);
|
||||
config_set_status(pwsc,session,NULL);
|
||||
db_dispose_item(pmp3);
|
||||
}
|
||||
config_set_status(pwsc,session,NULL);
|
||||
db_dispose_item(pmp3);
|
||||
} else {
|
||||
/**********************
|
||||
* stream file normally
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* $Id: $
|
||||
* Public plug-in interface
|
||||
*
|
||||
* Copyright (C) 2006 Ron Pedde (ron@pedde.com)
|
||||
*
|
||||
* 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 _FF_PLUGINS_H_
|
||||
#define _FF_PLUGINS_H_
|
||||
|
||||
/* Plugin types */
|
||||
#define PLUGIN_OUTPUT 1
|
||||
#define PLUGIN_SCANNER 2
|
||||
#define PLUGIN_DATABASE 4
|
||||
#define PLUGIN_EVENT 8
|
||||
#define PLUGIN_TRANSCODE 16
|
||||
|
||||
/* plugin event types */
|
||||
#define PLUGIN_EVENT_LOG 0
|
||||
#define PLUGIN_EVENT_FULLSCAN_START 1
|
||||
#define PLUGIN_EVENT_FULLSCAN_END 2
|
||||
#define PLUGIN_EVENT_STARTING 3
|
||||
#define PLUGIN_EVENT_SHUTDOWN 4
|
||||
#define PLUGIN_EVENT_STARTSTREAM 5
|
||||
#define PLUGIN_EVENT_ABORTSTREAM 6
|
||||
#define PLUGIN_EVENT_ENDSTREAM 7
|
||||
|
||||
#define PLUGIN_VERSION 1
|
||||
|
||||
#ifndef E_FATAL
|
||||
# define E_FATAL 0
|
||||
# define E_LOG 1
|
||||
# define E_INF 5
|
||||
# define E_DBG 9
|
||||
#endif
|
||||
|
||||
|
||||
struct tag_ws_conninfo;
|
||||
|
||||
/* Functions that must be exported by different plugin types */
|
||||
typedef struct tag_plugin_output_fn {
|
||||
void(*handler)(struct tag_ws_conninfo *pwsc);
|
||||
int(*auth)(struct tag_ws_conninfo *pwsc, char *username, char *pw);
|
||||
} PLUGIN_OUTPUT_FN;
|
||||
|
||||
typedef struct tag_plugin_event_fn {
|
||||
void(*handler)(int event_id, int intval, void *vp, int len);
|
||||
} PLUGIN_EVENT_FN;
|
||||
|
||||
typedef struct tag_plugin_transcode_fn {
|
||||
void *(*init)(void);
|
||||
void (*deinit)(void*);
|
||||
int (*open)(void*, char *, char*, int);
|
||||
int (*close)(void*);
|
||||
int (*read)(void*, char*, int);
|
||||
char *(*error)(void*);
|
||||
} PLUGIN_TRANSCODE_FN;
|
||||
|
||||
/* info for rendezvous advertising */
|
||||
typedef struct tag_plugin_rend_info {
|
||||
char *type;
|
||||
char *txt;
|
||||
} PLUGIN_REND_INFO;
|
||||
|
||||
/* main info struct that plugins must provide */
|
||||
typedef struct tag_plugin_info {
|
||||
int version; /* PLUGIN_VERSION */
|
||||
int type; /* PLUGIN_OUTPUT, etc */
|
||||
char *server; /* Server/version format */
|
||||
char *url; /* regex match of web urls */
|
||||
PLUGIN_OUTPUT_FN *output_fns; /* functions for different plugin types */
|
||||
PLUGIN_EVENT_FN *event_fns;
|
||||
PLUGIN_TRANSCODE_FN *transcode_fns;
|
||||
PLUGIN_REND_INFO *rend_info; /* array of rend announcements */
|
||||
char *codeclist; /* comma separated list of codecs */
|
||||
} PLUGIN_INFO;
|
||||
|
||||
|
||||
#define QUERY_TYPE_ITEMS 0
|
||||
#define QUERY_TYPE_PLAYLISTS 1
|
||||
#define QUERY_TYPE_DISTINCT 2
|
||||
|
||||
#define FILTER_TYPE_FIREFLY 0
|
||||
#define FILTER_TYPE_APPLE 1
|
||||
|
||||
typedef struct tag_db_query {
|
||||
int query_type;
|
||||
char *distinct_field;
|
||||
int filter_type;
|
||||
char *filter;
|
||||
|
||||
int offset;
|
||||
int limit;
|
||||
|
||||
int playlist_id; /* for items query */
|
||||
int totalcount; /* returned total count */
|
||||
void *private;
|
||||
} DB_QUERY;
|
||||
|
||||
|
||||
/* version 1 plugin imports */
|
||||
typedef struct tag_plugin_input_fn {
|
||||
/* webserver helpers */
|
||||
char* (*ws_uri)(struct tag_ws_conninfo *);
|
||||
void (*ws_close)(struct tag_ws_conninfo *);
|
||||
int (*ws_returnerror)(struct tag_ws_conninfo *, int, char *);
|
||||
char* (*ws_getvar)(struct tag_ws_conninfo *, char *);
|
||||
int (*ws_writefd)(struct tag_ws_conninfo *, char *, ...);
|
||||
int (*ws_addresponseheader)(struct tag_ws_conninfo *, char *, char *, ...);
|
||||
void (*ws_emitheaders)(struct tag_ws_conninfo *);
|
||||
int (*ws_fd)(struct tag_ws_conninfo *);
|
||||
char* (*ws_getrequestheader)(struct tag_ws_conninfo *, char *);
|
||||
int (*ws_writebinary)(struct tag_ws_conninfo *, char *, int);
|
||||
|
||||
/* misc helpers */
|
||||
char* (*server_ver)(void);
|
||||
int (*server_name)(char *, int *);
|
||||
void (*log)(int, char *, ...);
|
||||
|
||||
int (*db_count)(void);
|
||||
int (*db_enum_start)(char **, DB_QUERY *);
|
||||
int (*db_enum_fetch_row)(char **, char ***, DB_QUERY *);
|
||||
int (*db_enum_end)(char **);
|
||||
void (*db_enum_dispose)(char **, DB_QUERY*);
|
||||
void (*stream)(struct tag_ws_conninfo *, char *);
|
||||
|
||||
char *(*conf_alloc_string)(char *section, char *key, char *dflt);
|
||||
void (*conf_dispose_string)(char *str);
|
||||
} PLUGIN_INPUT_FN;
|
||||
|
||||
|
||||
#endif _FF_PLUGINS_
|
|
@ -77,7 +77,6 @@
|
|||
#include "mp3-scanner.h"
|
||||
#include "webserver.h"
|
||||
#include "restart.h"
|
||||
#include "ssc.h"
|
||||
#include "dynamic-art.h"
|
||||
#include "db-generic.h"
|
||||
#include "os.h"
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
#include "mp3-scanner.h"
|
||||
#include "os.h"
|
||||
#include "restart.h"
|
||||
#include "ssc.h"
|
||||
|
||||
/*
|
||||
* Typedefs
|
||||
|
|
252
src/plugin.c
252
src/plugin.c
|
@ -47,12 +47,8 @@
|
|||
|
||||
typedef struct tag_pluginentry {
|
||||
void *phandle;
|
||||
int type;
|
||||
char *versionstring;
|
||||
regex_t regex;
|
||||
PLUGIN_OUTPUT_FN *output_fns;
|
||||
PLUGIN_EVENT_FN *event_fns;
|
||||
PLUGIN_REND_INFO *rend_info;
|
||||
PLUGIN_INFO *pinfo;
|
||||
struct tag_pluginentry *next;
|
||||
} PLUGIN_ENTRY;
|
||||
|
||||
|
@ -60,7 +56,7 @@ typedef struct tag_pluginentry {
|
|||
static pthread_key_t _plugin_lock_key;
|
||||
static PLUGIN_ENTRY _plugin_list;
|
||||
static int _plugin_initialized = 0;
|
||||
|
||||
static char *_plugin_ssc_codecs = NULL;
|
||||
static pthread_rwlock_t _plugin_lock;
|
||||
|
||||
static char* _plugin_error_list[] = {
|
||||
|
@ -75,6 +71,7 @@ void _plugin_writelock(void);
|
|||
void _plugin_unlock(void);
|
||||
int _plugin_error(char **pe, int error, ...);
|
||||
void _plugin_free(int *pi);
|
||||
void _plugin_recalc_codecs(void);
|
||||
|
||||
/* webserver helpers */
|
||||
char *pi_ws_uri(WS_CONNINFO *pwsc);
|
||||
|
@ -270,6 +267,53 @@ int _plugin_error(char **pe, int error, ...) {
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* walk through the installed plugins and recalculate
|
||||
* the codec string
|
||||
*/
|
||||
void _plugin_recalc_codecs(void) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
int size=0;
|
||||
|
||||
_plugin_writelock();
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->pinfo->type & PLUGIN_TRANSCODE) {
|
||||
if(size) size++;
|
||||
size += strlen(ppi->pinfo->codeclist);
|
||||
}
|
||||
ppi=ppi->next;
|
||||
}
|
||||
|
||||
if(_plugin_ssc_codecs) {
|
||||
free(_plugin_ssc_codecs);
|
||||
}
|
||||
|
||||
_plugin_ssc_codecs = (char*)malloc(size+1);
|
||||
if(!_plugin_ssc_codecs) {
|
||||
DPRINTF(E_FATAL,L_PLUG,"_plugin_recalc_codecs: malloc\n");
|
||||
}
|
||||
|
||||
memset(_plugin_ssc_codecs,0,size+1);
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->pinfo->type & PLUGIN_TRANSCODE) {
|
||||
if(strlen(_plugin_ssc_codecs)) {
|
||||
strcat(_plugin_ssc_codecs,",");
|
||||
}
|
||||
strcat(_plugin_ssc_codecs,ppi->pinfo->codeclist);
|
||||
}
|
||||
ppi=ppi->next;
|
||||
}
|
||||
|
||||
DPRINTF(E_DBG,L_PLUG,"New transcode codec list: %s\n",_plugin_ssc_codecs);
|
||||
_plugin_unlock();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* load a specified plugin.
|
||||
*
|
||||
|
@ -281,7 +325,7 @@ int _plugin_error(char **pe, int error, ...) {
|
|||
int plugin_load(char **pe, char *path) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
void *phandle;
|
||||
PLUGIN_INFO *(*info_func)(void);
|
||||
PLUGIN_INFO *(*info_func)(PLUGIN_INPUT_FN *);
|
||||
PLUGIN_INFO *pinfo;
|
||||
|
||||
DPRINTF(E_DBG,L_PLUG,"Attempting to load plugin %s\n",path);
|
||||
|
@ -297,30 +341,35 @@ int plugin_load(char **pe, char *path) {
|
|||
|
||||
ppi->phandle = phandle;
|
||||
|
||||
info_func = (PLUGIN_INFO*(*)(void)) os_libfunc(pe, phandle,"plugin_info");
|
||||
info_func = (PLUGIN_INFO*(*)(PLUGIN_INPUT_FN*)) os_libfunc(pe, phandle,"plugin_info");
|
||||
if(info_func == NULL) {
|
||||
DPRINTF(E_INF,L_PLUG,"Couldn't get info_func for %s\n",path);
|
||||
os_unload(phandle);
|
||||
free(ppi);
|
||||
return PLUGIN_E_BADFUNCS;
|
||||
}
|
||||
|
||||
pinfo = info_func();
|
||||
pinfo = info_func(&pi);
|
||||
ppi->pinfo = pinfo;
|
||||
|
||||
ppi->type = pinfo->type;
|
||||
ppi->versionstring = pinfo->server;
|
||||
if(ppi->type & PLUGIN_OUTPUT) {
|
||||
if(!pinfo) {
|
||||
if(pe) *pe = strdup("plugin declined to load");
|
||||
os_unload(phandle);
|
||||
free(ppi);
|
||||
return PLUGIN_E_NOLOAD;
|
||||
}
|
||||
|
||||
if(pinfo->type & PLUGIN_OUTPUT) {
|
||||
/* build the regex */
|
||||
if(regcomp(&ppi->regex,pinfo->url,REG_EXTENDED | REG_NOSUB)) {
|
||||
DPRINTF(E_LOG,L_PLUG,"Bad regex in %s: %s\n",path,pinfo->url);
|
||||
}
|
||||
}
|
||||
ppi->output_fns = pinfo->output_fns;
|
||||
ppi->event_fns = pinfo->event_fns;
|
||||
ppi->rend_info = pinfo->rend_info;
|
||||
|
||||
DPRINTF(E_INF,L_PLUG,"Loaded plugin %s (%s)\n",path,ppi->versionstring);
|
||||
pinfo->pi = (void*)π
|
||||
|
||||
DPRINTF(E_INF,L_PLUG,"Loaded plugin %s (%s)\n",path,pinfo->server);
|
||||
|
||||
_plugin_writelock();
|
||||
|
||||
if(!_plugin_initialized) {
|
||||
_plugin_initialized = 1;
|
||||
memset((void*)&_plugin_list,0,sizeof(_plugin_list));
|
||||
|
@ -331,6 +380,7 @@ int plugin_load(char **pe, char *path) {
|
|||
|
||||
_plugin_unlock();
|
||||
|
||||
_plugin_recalc_codecs();
|
||||
return PLUGIN_E_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -348,7 +398,7 @@ int plugin_url_candispatch(WS_CONNINFO *pwsc) {
|
|||
_plugin_readlock();
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->type & PLUGIN_OUTPUT) {
|
||||
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
|
||||
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
|
||||
/* we have a winner */
|
||||
_plugin_unlock();
|
||||
|
@ -375,14 +425,14 @@ void plugin_url_handle(WS_CONNINFO *pwsc) {
|
|||
_plugin_readlock();
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->type & PLUGIN_OUTPUT) {
|
||||
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
|
||||
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
|
||||
/* we have a winner */
|
||||
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", pwsc->uri,
|
||||
ppi->versionstring);
|
||||
ppi->pinfo->server);
|
||||
|
||||
/* so functions must be a tag_plugin_output_fn */
|
||||
disp_fn=(ppi->output_fns)->handler;
|
||||
disp_fn=(ppi->pinfo->output_fns)->handler;
|
||||
disp_fn(pwsc);
|
||||
_plugin_unlock();
|
||||
return;
|
||||
|
@ -407,7 +457,6 @@ int plugin_rend_register(char *name, int port, char *iface, char *txt) {
|
|||
char *supplied_txt;
|
||||
char *new_name;
|
||||
char *ver;
|
||||
char *slash;
|
||||
int name_len;
|
||||
|
||||
|
||||
|
@ -415,9 +464,9 @@ int plugin_rend_register(char *name, int port, char *iface, char *txt) {
|
|||
ppi = _plugin_list.next;
|
||||
|
||||
while(ppi) {
|
||||
DPRINTF(E_DBG,L_PLUG,"Checking %s\n",ppi->versionstring);
|
||||
if(ppi->rend_info) {
|
||||
pri = ppi->rend_info;
|
||||
DPRINTF(E_DBG,L_PLUG,"Checking %s\n",ppi->pinfo->server);
|
||||
if(ppi->pinfo->rend_info) {
|
||||
pri = ppi->pinfo->rend_info;
|
||||
while(pri->type) {
|
||||
supplied_txt = pri->txt;
|
||||
if(!pri->txt)
|
||||
|
@ -425,7 +474,7 @@ int plugin_rend_register(char *name, int port, char *iface, char *txt) {
|
|||
|
||||
DPRINTF(E_DBG,L_PLUG,"Registering %s\n",pri->type);
|
||||
|
||||
name_len = (int)strlen(name) + 4 + (int)strlen(ppi->versionstring);
|
||||
name_len = (int)strlen(name) + 4 + (int)strlen(ppi->pinfo->server);
|
||||
new_name=(char*)malloc(name_len);
|
||||
if(!new_name)
|
||||
DPRINTF(E_FATAL,L_PLUG,"plugin_rend_register: malloc");
|
||||
|
@ -433,7 +482,7 @@ int plugin_rend_register(char *name, int port, char *iface, char *txt) {
|
|||
memset(new_name,0,name_len);
|
||||
|
||||
if(conf_get_int("plugins","mangle_rendezvous",1)) {
|
||||
ver = strdup(ppi->versionstring);
|
||||
ver = strdup(ppi->pinfo->server);
|
||||
if(strchr(ver,'/')) {
|
||||
*strchr(ver,'/') = '\0';
|
||||
}
|
||||
|
@ -472,14 +521,14 @@ int plugin_auth_handle(WS_CONNINFO *pwsc, char *username, char *pw) {
|
|||
_plugin_readlock();
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->type & PLUGIN_OUTPUT) {
|
||||
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
|
||||
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
|
||||
/* we have a winner */
|
||||
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", pwsc->uri,
|
||||
ppi->versionstring);
|
||||
ppi->pinfo->server);
|
||||
|
||||
/* so functions must be a tag_plugin_output_fn */
|
||||
auth_fn=(ppi->output_fns)->auth;
|
||||
auth_fn=(ppi->pinfo->output_fns)->auth;
|
||||
if(auth_fn) {
|
||||
result=auth_fn(pwsc,username,pw);
|
||||
_plugin_unlock();
|
||||
|
@ -505,27 +554,150 @@ int plugin_auth_handle(WS_CONNINFO *pwsc, char *username, char *pw) {
|
|||
void plugin_event_dispatch(int event_id, int intval, void *vp, int len) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
|
||||
fprintf(stderr,"entering plugin_event_dispatch\n");
|
||||
|
||||
// _plugin_readlock();
|
||||
_plugin_readlock();
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
fprintf(stderr,"Checking %s\n",ppi->versionstring);
|
||||
if(ppi->type & PLUGIN_EVENT) {
|
||||
fprintf(stderr,"Checking %s\n",ppi->pinfo->server);
|
||||
if(ppi->pinfo->type & PLUGIN_EVENT) {
|
||||
/* DPRINTF(E_DBG,L_PLUG,"Dispatching event %d to %s\n",
|
||||
event_id,ppi->versionstring); */
|
||||
|
||||
if((ppi->event_fns) && (ppi->event_fns->handler)) {
|
||||
ppi->event_fns->handler(event_id, intval, vp, len);
|
||||
if((ppi->pinfo->event_fns) && (ppi->pinfo->event_fns->handler)) {
|
||||
ppi->pinfo->event_fns->handler(event_id, intval, vp, len);
|
||||
}
|
||||
}
|
||||
ppi=ppi->next;
|
||||
}
|
||||
// _plugin_unlock();
|
||||
_plugin_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* check to see if we can transcode
|
||||
*
|
||||
* @param codec the codec we are trying to serve
|
||||
* @returns TRUE if we can transcode, FALSE otherwise
|
||||
*/
|
||||
int plugin_ssc_can_transcode(char *codec) {
|
||||
int result;
|
||||
|
||||
_plugin_readlock();
|
||||
result = FALSE;
|
||||
if(strstr(_plugin_ssc_codecs,codec)) {
|
||||
result = TRUE;
|
||||
}
|
||||
_plugin_unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* stupid helper to copy transcode stream to the fd
|
||||
*/
|
||||
int _plugin_ssc_copy(WS_CONNINFO *pwsc, PLUGIN_TRANSCODE_FN *pfn,
|
||||
void *vp,int offset) {
|
||||
int bytes_read;
|
||||
int bytes_to_read;
|
||||
int total_bytes_read = 0;
|
||||
char buffer[1024];
|
||||
|
||||
/* first, skip past the offset */
|
||||
while(offset) {
|
||||
bytes_to_read = sizeof(buffer);
|
||||
if(bytes_to_read > offset)
|
||||
bytes_to_read = offset;
|
||||
|
||||
bytes_read = pfn->read(vp,buffer,bytes_to_read);
|
||||
if(bytes_read <= 0)
|
||||
return bytes_read;
|
||||
|
||||
offset -= bytes_read;
|
||||
}
|
||||
|
||||
while((bytes_read=pfn->read(vp,buffer,sizeof(buffer))) > 0) {
|
||||
total_bytes_read += bytes_read;
|
||||
ws_writebinary(pwsc,buffer,bytes_read);
|
||||
}
|
||||
|
||||
if(bytes_read < 0)
|
||||
return bytes_read;
|
||||
|
||||
return total_bytes_read;
|
||||
}
|
||||
|
||||
/**
|
||||
* do the transcode, emitting the headers, content type,
|
||||
* and shoving the file down the wire
|
||||
*
|
||||
* @param pwsc connection to transcode to
|
||||
* @param file file to transcode
|
||||
* @param codec source codec
|
||||
* @param duration time in ms
|
||||
* @returns bytes transferred, or -1 on error
|
||||
*/
|
||||
int plugin_ssc_transcode(WS_CONNINFO *pwsc, char *file, char *codec, int duration, int offset) {
|
||||
PLUGIN_ENTRY *ppi, *ptc=NULL;
|
||||
PLUGIN_TRANSCODE_FN *pfn = NULL;
|
||||
void *vp_ssc;
|
||||
int post_error = 1;
|
||||
int result = -1;
|
||||
|
||||
/* first, find the plugin that will do the conversion */
|
||||
|
||||
_plugin_readlock();
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
while((ppi) && (!pfn)) {
|
||||
if(ppi->pinfo->type & PLUGIN_TRANSCODE) {
|
||||
if(strstr(ppi->pinfo->codeclist,codec)) {
|
||||
ptc = ppi;
|
||||
pfn = ppi->pinfo->transcode_fns;
|
||||
}
|
||||
}
|
||||
ppi = ppi->next;
|
||||
}
|
||||
|
||||
if(pfn) {
|
||||
DPRINTF(E_DBG,L_PLUG,"Transcoding %s with %s\n",file,
|
||||
ptc->pinfo->server);
|
||||
|
||||
vp_ssc = pfn->init();
|
||||
if(vp_ssc) {
|
||||
if(pfn->open(vp_ssc,file,codec,duration)) {
|
||||
/* start reading and throwing */
|
||||
ws_addresponseheader(pwsc,"Content-Type","audio/wav");
|
||||
ws_addresponseheader(pwsc,"Connection","Close");
|
||||
if(!offset) {
|
||||
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
||||
} else {
|
||||
ws_addresponseheader(pwsc,"Content-Range","bytes %ld-*/*",
|
||||
(long)offset);
|
||||
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
|
||||
}
|
||||
ws_emitheaders(pwsc);
|
||||
|
||||
/* start reading/writing */
|
||||
result = _plugin_ssc_copy(pwsc,pfn,vp_ssc,offset);
|
||||
post_error = 0;
|
||||
pfn->close(vp_ssc);
|
||||
} else {
|
||||
DPRINTF(E_LOG,L_PLUG,"Error opening %s for ssc: %s\n",
|
||||
file,pfn->error(vp_ssc));
|
||||
}
|
||||
pfn->deinit(vp_ssc);
|
||||
} else {
|
||||
DPRINTF(E_LOG,L_PLUG,"Error initializing transcoder: %s\n",
|
||||
ptc->pinfo->server);
|
||||
}
|
||||
}
|
||||
|
||||
if(post_error) {
|
||||
pwsc->error = EPERM; /* ?? */
|
||||
ws_returnerror(pwsc,500,"Internal error");
|
||||
}
|
||||
|
||||
_plugin_unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* plugin wrappers for utility functions & stuff
|
||||
*
|
||||
|
@ -646,6 +818,8 @@ int pi_db_enum_start(char **pe, DB_QUERY *pinfo) {
|
|||
pqi->playlist_id = pinfo->playlist_id;
|
||||
result = db_enum_start(pe, pqi);
|
||||
pinfo->totalcount = pqi->specifiedtotalcount;
|
||||
|
||||
return DB_E_SUCCESS;
|
||||
}
|
||||
|
||||
int pi_db_enum_fetch_row(char **pe, char ***row, DB_QUERY *pinfo) {
|
||||
|
|
|
@ -37,6 +37,10 @@ extern int plugin_auth_handle(WS_CONNINFO *pwsc, char *username, char *pw);
|
|||
extern int plugin_rend_register(char *name, int port, char *iface, char *txt);
|
||||
extern void plugin_event_dispatch(int event_id, int intval, void *vp, int len);
|
||||
|
||||
/* these should really get rows */
|
||||
extern int plugin_ssc_can_transcode(char *codec);
|
||||
extern int plugin_ssc_transcode(WS_CONNINFO *pwsc, char *file, char *codec, int duration, int offset);
|
||||
|
||||
#define PLUGIN_E_SUCCESS 0
|
||||
#define PLUGIN_E_NOLOAD 1
|
||||
#define PLUGIN_E_BADFUNCS 2
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#
|
||||
rspdir = ${pkgdatadir}/plugins
|
||||
ssc_ffmpegdir = ${pkgdatadir}/plugins
|
||||
ssc_scriptdir = ${pkgdatadir}/plugins
|
||||
|
||||
rsp_LTLIBRARIES=rsp.la
|
||||
rsp_la_LDFLAGS=-module -avoid-version
|
||||
|
@ -13,7 +14,11 @@ ssc_ffmpeg_la_LDFLAGS=-module -avoid-version
|
|||
ssc_ffmpeg_la_SOURCES=ssc-ffmpeg.c
|
||||
endif
|
||||
|
||||
EXTRA_DIST = compat.h rsp.h mtd-plugins.h xml-rpc.h ssc-ffmpeg.c
|
||||
ssc_script_LTLIBRARIES=ssc-script.la
|
||||
ssc_script_la_LDFLAGS=-module -avoid-version
|
||||
ssc_script_la_SOURCES=ssc-script.c
|
||||
|
||||
EXTRA_DIST = compat.h rsp.h mtd-plugins.h xml-rpc.h ssc-ffmpeg.c ssc-script.c
|
||||
|
||||
AM_CFLAGS = -I..
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ typedef struct tag_rsp_privinfo {
|
|||
} PRIVINFO;
|
||||
|
||||
/* Forwards */
|
||||
PLUGIN_INFO *plugin_info(void);
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *);
|
||||
void plugin_handler(WS_CONNINFO *pwsc);
|
||||
int plugin_auth(WS_CONNINFO *pwsc, char *username, char *pw);
|
||||
void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi);
|
||||
|
@ -39,6 +39,8 @@ PLUGIN_REND_INFO _pri[] = {
|
|||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
PLUGIN_INPUT_FN *_ppi;
|
||||
|
||||
PLUGIN_INFO _pi = {
|
||||
PLUGIN_VERSION, /* version */
|
||||
PLUGIN_OUTPUT, /* type */
|
||||
|
@ -47,7 +49,6 @@ PLUGIN_INFO _pi = {
|
|||
&_pofn, /* output fns */
|
||||
NULL, /* event fns */
|
||||
NULL, /* transcode fns */
|
||||
NULL, /* ff functions */
|
||||
_pri, /* rend info */
|
||||
NULL /* transcode info */
|
||||
};
|
||||
|
@ -145,7 +146,8 @@ FIELDSPEC rsp_fields[] = {
|
|||
/**
|
||||
* return info about this plugin module
|
||||
*/
|
||||
PLUGIN_INFO *plugin_info(void) {
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *ppi) {
|
||||
_ppi = ppi;
|
||||
return &_pi;
|
||||
}
|
||||
|
||||
|
@ -168,7 +170,7 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
|||
int index, part;
|
||||
int found;
|
||||
|
||||
string = infn->ws_uri(pwsc);
|
||||
string = _ppi->ws_uri(pwsc);
|
||||
string++;
|
||||
|
||||
ppi = (PRIVINFO *)malloc(sizeof(PRIVINFO));
|
||||
|
@ -177,27 +179,27 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
|||
}
|
||||
|
||||
if(!ppi) {
|
||||
infn->ws_returnerror(pwsc,500,"Malloc error in plugin_handler");
|
||||
_ppi->ws_returnerror(pwsc,500,"Malloc error in plugin_handler");
|
||||
return;
|
||||
}
|
||||
|
||||
memset((void*)&ppi->dq,0,sizeof(DB_QUERY));
|
||||
|
||||
infn->log(E_DBG,"Tokenizing url\n");
|
||||
_ppi->log(E_DBG,"Tokenizing url\n");
|
||||
while((ppi->uri_count < 10) && (token=strtok_r(string,"/",&save))) {
|
||||
string=NULL;
|
||||
ppi->uri_sections[ppi->uri_count++] = token;
|
||||
}
|
||||
|
||||
elements = sizeof(rsp_uri_map) / sizeof(PLUGIN_RESPONSE);
|
||||
infn->log(E_DBG,"Found %d elements\n",elements);
|
||||
_ppi->log(E_DBG,"Found %d elements\n",elements);
|
||||
|
||||
index = 0;
|
||||
found = 0;
|
||||
|
||||
while((!found) && (index < elements)) {
|
||||
/* test this set */
|
||||
infn->log(E_DBG,"Checking reponse %d\n",index);
|
||||
_ppi->log(E_DBG,"Checking reponse %d\n",index);
|
||||
part=0;
|
||||
while(part < 10) {
|
||||
if((rsp_uri_map[index].uri[part]) && (!ppi->uri_sections[part]))
|
||||
|
@ -216,7 +218,7 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
|||
|
||||
if(part == 10) {
|
||||
found = 1;
|
||||
infn->log(E_DBG,"Found it! Index: %d\n",index);
|
||||
_ppi->log(E_DBG,"Found it! Index: %d\n",index);
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
|
@ -224,13 +226,13 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
|||
|
||||
if(found) {
|
||||
rsp_uri_map[index].dispatch(pwsc, ppi);
|
||||
infn->ws_close(pwsc);
|
||||
_ppi->ws_close(pwsc);
|
||||
free(ppi);
|
||||
return;
|
||||
}
|
||||
|
||||
rsp_error(pwsc, ppi, 1, "Bad path");
|
||||
infn->ws_close(pwsc);
|
||||
_ppi->ws_close(pwsc);
|
||||
free(ppi);
|
||||
return;
|
||||
}
|
||||
|
@ -243,7 +245,7 @@ void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
char servername[256];
|
||||
int size;
|
||||
|
||||
infn->log(E_DBG,"Starting rsp_info\n");
|
||||
_ppi->log(E_DBG,"Starting rsp_info\n");
|
||||
|
||||
pxml = xml_init(pwsc,1);
|
||||
|
||||
|
@ -257,13 +259,13 @@ void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
|
||||
/* info block */
|
||||
xml_push(pxml,"info");
|
||||
xml_output(pxml,"count","%d",infn->db_count());
|
||||
xml_output(pxml,"count","%d",_ppi->db_count());
|
||||
xml_output(pxml,"rsp-version",RSP_VERSION);
|
||||
|
||||
xml_output(pxml,"server-version",infn->server_ver());
|
||||
xml_output(pxml,"server-version",_ppi->server_ver());
|
||||
|
||||
size = sizeof(servername);
|
||||
infn->server_name(servername,&size);
|
||||
_ppi->server_name(servername,&size);
|
||||
xml_output(pxml,"name",servername);
|
||||
xml_pop(pxml); /* info */
|
||||
|
||||
|
@ -285,9 +287,9 @@ void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
|
||||
ppi->dq.query_type = QUERY_TYPE_PLAYLISTS;
|
||||
|
||||
if((err=infn->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
if((err=_ppi->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
rsp_error(pwsc, ppi, err | E_DB, pe);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -303,7 +305,7 @@ void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
|
||||
xml_push(pxml,"playlists");
|
||||
|
||||
while((infn->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
while((_ppi->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
xml_push(pxml,"playlist");
|
||||
rowindex=0;
|
||||
while(rsp_playlist_fields[rowindex].name) {
|
||||
|
@ -316,8 +318,8 @@ void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
xml_pop(pxml); /* playlist */
|
||||
}
|
||||
|
||||
infn->db_enum_end(NULL);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_end(NULL);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
|
||||
xml_pop(pxml); /* playlists */
|
||||
xml_pop(pxml); /* response */
|
||||
|
@ -343,7 +345,7 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
// char *user_agent;
|
||||
|
||||
/*
|
||||
user_agent = infn->ws_getrequestheader(pwsc,"user-agent");
|
||||
user_agent = _ppi->ws_getrequestheader(pwsc,"user-agent");
|
||||
if(user_agent) {
|
||||
if(strncmp(user_agent,"iTunes",6)==0) {
|
||||
trancode_codecs = "wma,ogg,flac,mpc";
|
||||
|
@ -355,17 +357,17 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
}
|
||||
*/
|
||||
|
||||
ppi->dq.filter = infn->ws_getvar(pwsc,"query");
|
||||
ppi->dq.filter = _ppi->ws_getvar(pwsc,"query");
|
||||
ppi->dq.filter_type = FILTER_TYPE_FIREFLY;
|
||||
|
||||
if(infn->ws_getvar(pwsc,"offset")) {
|
||||
ppi->dq.offset = atoi(infn->ws_getvar(pwsc,"offset"));
|
||||
if(_ppi->ws_getvar(pwsc,"offset")) {
|
||||
ppi->dq.offset = atoi(_ppi->ws_getvar(pwsc,"offset"));
|
||||
}
|
||||
if(infn->ws_getvar(pwsc,"limit")) {
|
||||
ppi->dq.limit = atoi(infn->ws_getvar(pwsc,"limit"));
|
||||
if(_ppi->ws_getvar(pwsc,"limit")) {
|
||||
ppi->dq.limit = atoi(_ppi->ws_getvar(pwsc,"limit"));
|
||||
}
|
||||
|
||||
browse_type = infn->ws_getvar(pwsc,"type");
|
||||
browse_type = _ppi->ws_getvar(pwsc,"type");
|
||||
type = F_FULL;
|
||||
|
||||
if(browse_type) {
|
||||
|
@ -378,9 +380,9 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
ppi->dq.query_type = QUERY_TYPE_ITEMS;
|
||||
ppi->dq.playlist_id = atoi(ppi->uri_sections[2]);
|
||||
|
||||
if((err=infn->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
if((err=_ppi->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
rsp_error(pwsc, ppi, err | E_DB, pe);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
free(pe);
|
||||
return;
|
||||
}
|
||||
|
@ -395,7 +397,7 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
returned = ppi->dq.totalcount - ppi->dq.offset;
|
||||
}
|
||||
|
||||
transcode_codecs = infn->conf_alloc_string("general","ssc_codectypes","");
|
||||
transcode_codecs = _ppi->conf_alloc_string("general","ssc_codectypes","");
|
||||
|
||||
xml_push(pxml,"response");
|
||||
xml_push(pxml,"status");
|
||||
|
@ -407,7 +409,7 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
|
||||
xml_push(pxml,"items");
|
||||
|
||||
while((infn->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
while((_ppi->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
xml_push(pxml,"item");
|
||||
rowindex=0;
|
||||
transcode = 0;
|
||||
|
@ -453,8 +455,8 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
xml_pop(pxml); /* item */
|
||||
}
|
||||
|
||||
infn->db_enum_end(NULL);
|
||||
infn->conf_dispose_string(transcode_codecs);
|
||||
_ppi->db_enum_end(NULL);
|
||||
_ppi->conf_dispose_string(transcode_codecs);
|
||||
|
||||
xml_pop(pxml); /* items */
|
||||
xml_pop(pxml); /* response */
|
||||
|
@ -471,22 +473,22 @@ void rsp_browse(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
/* this might fail if an unsupported browse type */
|
||||
ppi->dq.query_type = QUERY_TYPE_DISTINCT;
|
||||
ppi->dq.distinct_field = ppi->uri_sections[3];
|
||||
ppi->dq.filter = infn->ws_getvar(pwsc,"query");
|
||||
ppi->dq.filter = _ppi->ws_getvar(pwsc,"query");
|
||||
ppi->dq.filter_type = FILTER_TYPE_FIREFLY;
|
||||
|
||||
if(infn->ws_getvar(pwsc,"offset")) {
|
||||
ppi->dq.offset = atoi(infn->ws_getvar(pwsc,"offset"));
|
||||
if(_ppi->ws_getvar(pwsc,"offset")) {
|
||||
ppi->dq.offset = atoi(_ppi->ws_getvar(pwsc,"offset"));
|
||||
}
|
||||
|
||||
if(infn->ws_getvar(pwsc,"limit")) {
|
||||
ppi->dq.limit = atoi(infn->ws_getvar(pwsc,"limit"));
|
||||
if(_ppi->ws_getvar(pwsc,"limit")) {
|
||||
ppi->dq.limit = atoi(_ppi->ws_getvar(pwsc,"limit"));
|
||||
}
|
||||
|
||||
ppi->dq.playlist_id = atoi(ppi->uri_sections[2]);
|
||||
|
||||
if((err=infn->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
if((err=_ppi->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
rsp_error(pwsc, ppi, err | E_DB, pe);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -510,12 +512,12 @@ void rsp_browse(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
|
||||
xml_push(pxml,"items");
|
||||
|
||||
while((infn->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
while((_ppi->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
xml_output(pxml,"item",row[0]);
|
||||
}
|
||||
|
||||
infn->db_enum_end(NULL);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_end(NULL);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
|
||||
xml_pop(pxml); /* items */
|
||||
xml_pop(pxml); /* response */
|
||||
|
@ -523,7 +525,7 @@ void rsp_browse(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
|||
}
|
||||
|
||||
void rsp_stream(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
infn->stream(pwsc, ppi->uri_sections[2]);
|
||||
_ppi->stream(pwsc, ppi->uri_sections[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -540,6 +542,6 @@ void rsp_error(WS_CONNINFO *pwsc, PRIVINFO *ppi, int eno, char *estr) {
|
|||
xml_pop(pxml); /* status */
|
||||
xml_pop(pxml); /* response */
|
||||
xml_deinit(pxml);
|
||||
infn->ws_close(pwsc);
|
||||
_ppi->ws_close(pwsc);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#define RSP_VERSION "1.0"
|
||||
|
||||
extern PLUGIN_INFO _pi;
|
||||
#define infn ((PLUGIN_INPUT_FN *)(_pi.pi))
|
||||
|
||||
#ifndef TRUE
|
||||
# define TRUE 1
|
||||
|
|
|
@ -40,6 +40,8 @@ typedef struct tag_ssc_handle {
|
|||
int buf_remainder_len;
|
||||
int first_frame;
|
||||
|
||||
int duration;
|
||||
|
||||
int total_decoded;
|
||||
int total_written;
|
||||
|
||||
|
@ -64,19 +66,27 @@ typedef struct tag_ssc_handle {
|
|||
#define SSC_FFMPEG_E_FILEOPEN 3
|
||||
#define SSC_FFMPEG_E_NOSTREAM 4
|
||||
#define SSC_FFMPEG_E_NOAUDIO 5
|
||||
#define SSC_FFMPEG_E_BADFORMAT 6
|
||||
|
||||
char *ssc_ffmpeg_errors[] = {
|
||||
"Success",
|
||||
"Don't have appropriate codec",
|
||||
"Can't open codec",
|
||||
"Cannot open file",
|
||||
"Cannot find any streams",
|
||||
"No audio streams"
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Forwards */
|
||||
void *ssc_ffmpeg_init(void);
|
||||
void ssc_ffmpeg_deinit(void *pv);
|
||||
int ssc_ffmpeg_open(void *pv, char *file, char *codec);
|
||||
int ssc_ffmpeg_open(void *pv, char *file, char *codec, int duration);
|
||||
int ssc_ffmpeg_close(void *pv);
|
||||
int ssc_ffmpeg_read(void *pv, char *buffer, int len);
|
||||
char *ssc_ffmpeg_error(void *pv);
|
||||
|
||||
PLUGIN_INFO *plugin_info(void);
|
||||
|
||||
#define infn ((PLUGIN_INPUT_FN *)(_pi.pi))
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN*);
|
||||
|
||||
/* Globals */
|
||||
PLUGIN_TRANSCODE_FN _ptfn = {
|
||||
|
@ -84,9 +94,12 @@ PLUGIN_TRANSCODE_FN _ptfn = {
|
|||
ssc_ffmpeg_deinit,
|
||||
ssc_ffmpeg_open,
|
||||
ssc_ffmpeg_close,
|
||||
ssc_ffmpeg_read
|
||||
ssc_ffmpeg_read,
|
||||
ssc_ffmpeg_error
|
||||
};
|
||||
|
||||
PLUGIN_INPUT_FN *_ppi;
|
||||
|
||||
PLUGIN_INFO _pi = {
|
||||
PLUGIN_VERSION, /* version */
|
||||
PLUGIN_TRANSCODE, /* type */
|
||||
|
@ -95,12 +108,18 @@ PLUGIN_INFO _pi = {
|
|||
NULL, /* output fns */
|
||||
NULL, /* event fns */
|
||||
&_ptfn, /* fns */
|
||||
NULL, /* functions exported by ff */
|
||||
NULL, /* rend info */
|
||||
"flac,alac,ogg,wma" /* codeclist */
|
||||
};
|
||||
|
||||
PLUGIN_INFO *plugin_info(void) {
|
||||
char *ssc_ffmpeg_error(void *pv) {
|
||||
SSCHANDLE *handle = (SSCHANDLE*)pv;
|
||||
|
||||
return ssc_ffmpeg_errors[handle->errno];
|
||||
}
|
||||
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *ppi) {
|
||||
_ppi = ppi;
|
||||
av_register_all();
|
||||
|
||||
return &_pi;
|
||||
|
@ -127,7 +146,7 @@ void ssc_ffmpeg_deinit(void *vp) {
|
|||
return;
|
||||
}
|
||||
|
||||
int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||
int ssc_ffmpeg_open(void *vp, char *file, char *codec, int duration) {
|
||||
int i;
|
||||
enum CodecID id=CODEC_ID_FLAC;
|
||||
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
||||
|
@ -135,10 +154,11 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
|||
if(!handle)
|
||||
return FALSE;
|
||||
|
||||
handle->duration = duration;
|
||||
handle->first_frame = 1;
|
||||
handle->raw=0;
|
||||
|
||||
infn->log(E_DBG,"opening %s\n",file);
|
||||
_ppi->log(E_DBG,"opening %s\n",file);
|
||||
|
||||
if(strcasecmp(codec,"flac") == 0) {
|
||||
handle->raw=1;
|
||||
|
@ -146,6 +166,7 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
|||
}
|
||||
|
||||
if(handle->raw) {
|
||||
_ppi->log(E_DBG,"opening file raw\n");
|
||||
handle->pCodec = avcodec_find_decoder(id);
|
||||
if(!handle->pCodec) {
|
||||
handle->errno = SSC_FFMPEG_E_BADCODEC;
|
||||
|
@ -166,7 +187,8 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
|||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
_ppi->log(E_DBG,"opening file with format\n");
|
||||
if(av_open_input_file(&handle->pFmtCtx,file,handle->pFormat,0,NULL) < 0) {
|
||||
handle->errno = SSC_FFMPEG_E_FILEOPEN;
|
||||
return FALSE;
|
||||
|
@ -178,7 +200,7 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
dump_format(handle->pFmtCtx,0,file,FALSE);
|
||||
// dump_format(handle->pFmtCtx,0,file,FALSE);
|
||||
|
||||
handle->audio_stream = -1;
|
||||
for(i=0; i < handle->pFmtCtx->nb_streams; i++) {
|
||||
|
@ -226,8 +248,10 @@ int ssc_ffmpeg_close(void *vp) {
|
|||
if(handle->pFrame)
|
||||
av_free(handle->pFrame);
|
||||
|
||||
if(handle->pCodecCtx)
|
||||
avcodec_close(handle->pCodecCtx);
|
||||
if(handle->raw) {
|
||||
if(handle->pCodecCtx)
|
||||
avcodec_close(handle->pCodecCtx);
|
||||
}
|
||||
|
||||
if(handle->pFmtCtx)
|
||||
av_close_input_file(handle->pFmtCtx);
|
||||
|
@ -393,14 +417,17 @@ int ssc_ffmpeg_read(void *vp, char *buffer, int len) {
|
|||
handle->swab = (bits_per_sample == 16) &&
|
||||
(memcmp((void*)&test1,test2,2) == 0);
|
||||
|
||||
data_len = (bits_per_sample * sample_rate * channels * (duration/1000));
|
||||
if(handle->duration)
|
||||
duration = handle->duration;
|
||||
|
||||
data_len = ((bits_per_sample * sample_rate * channels / 8) * (duration/1000));
|
||||
byte_rate = sample_rate * channels * bits_per_sample / 8;
|
||||
block_align = channels * bits_per_sample / 8;
|
||||
|
||||
infn->log(E_DBG,"Channels.......: %d\n",channels);
|
||||
infn->log(E_DBG,"Sample rate....: %d\n",sample_rate);
|
||||
infn->log(E_DBG,"Bits/Sample....: %d\n",bits_per_sample);
|
||||
infn->log(E_DBG,"Swab...........: %d\n",handle->swab);
|
||||
_ppi->log(E_DBG,"Channels.......: %d\n",channels);
|
||||
_ppi->log(E_DBG,"Sample rate....: %d\n",sample_rate);
|
||||
_ppi->log(E_DBG,"Bits/Sample....: %d\n",bits_per_sample);
|
||||
_ppi->log(E_DBG,"Swab...........: %d\n",handle->swab);
|
||||
|
||||
memcpy(&handle->wav_header[0],"RIFF",4);
|
||||
_ssc_ffmpeg_le32(&handle->wav_header[4],36 + data_len);
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* $Id: $
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ff-plugins.h"
|
||||
|
||||
#ifndef TRUE
|
||||
# define TRUE 1
|
||||
# define FALSE 0
|
||||
#endif
|
||||
|
||||
/* Forwards */
|
||||
void *ssc_script_init(void);
|
||||
void ssc_script_deinit(void *vp);
|
||||
int ssc_script_open(void *vp, char *file, char *codec, int duration);
|
||||
int ssc_script_close(void *vp);
|
||||
int ssc_script_read(void *vp, char *buffer, int len);
|
||||
char *ssc_script_error(void *vp);
|
||||
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *);
|
||||
|
||||
#define infn ((PLUGIN_INPUT_FN *)(_pi.pi))
|
||||
|
||||
/* Globals */
|
||||
PLUGIN_TRANSCODE_FN _ptfn = {
|
||||
ssc_script_init,
|
||||
ssc_script_deinit,
|
||||
ssc_script_open,
|
||||
ssc_script_close,
|
||||
ssc_script_read,
|
||||
ssc_script_error
|
||||
};
|
||||
|
||||
PLUGIN_INPUT_FN *_ppi;
|
||||
|
||||
PLUGIN_INFO _pi = {
|
||||
PLUGIN_VERSION, /* version */
|
||||
PLUGIN_TRANSCODE, /* type */
|
||||
"ssc-script/" VERSION, /* server */
|
||||
NULL, /* url */
|
||||
NULL, /* output fns */
|
||||
NULL, /* event fns */
|
||||
&_ptfn, /* fns */
|
||||
NULL, /* rend info */
|
||||
NULL /* codeclist */
|
||||
};
|
||||
|
||||
typedef struct tag_ssc_handle {
|
||||
FILE *fin;
|
||||
} SSCHANDLE;
|
||||
|
||||
static char *_ssc_script_program = NULL;
|
||||
|
||||
/**
|
||||
* return the plugininfo struct to firefly
|
||||
*/
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *ppi) {
|
||||
char *codeclist;
|
||||
|
||||
_ppi = ppi;
|
||||
|
||||
_ssc_script_program = _ppi->conf_alloc_string("general","ssc_prog",NULL);
|
||||
if(!_ssc_script_program) {
|
||||
_ppi->log(E_INF,"No ssc program specified for script transcoder.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: need an unload function to stop leak */
|
||||
codeclist = _ppi->conf_alloc_string("general","ssc_codectypes",NULL);
|
||||
if(!codeclist) {
|
||||
_ppi->log(E_INF,"No codectypes specified for script transcoder.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_pi.codeclist = codeclist;
|
||||
return &_pi;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* get a new transcode handle
|
||||
*/
|
||||
void *ssc_script_init(void) {
|
||||
SSCHANDLE *handle;
|
||||
|
||||
handle = (SSCHANDLE*)malloc(sizeof(SSCHANDLE));
|
||||
if(handle) {
|
||||
memset(handle,0,sizeof(SSCHANDLE));
|
||||
}
|
||||
|
||||
return (void*)handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: make register errors in the sschandle
|
||||
*/
|
||||
char *ssc_script_error(void *vp) {
|
||||
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dispose of the transocde handle obtained from init
|
||||
*
|
||||
* @param pv handle to dispose
|
||||
*/
|
||||
void ssc_script_deinit(void *vp) {
|
||||
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
||||
|
||||
if(handle->fin) {
|
||||
pclose(handle->fin);
|
||||
}
|
||||
|
||||
if(handle)
|
||||
free(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* open a file to transocde
|
||||
*
|
||||
* @param pv private sschandle obtained from init
|
||||
* @param file file name to transcode
|
||||
* @param codec codec type
|
||||
* @param duration duration in ms
|
||||
*/
|
||||
int ssc_script_open(void *vp, char *file, char *codec, int duration) {
|
||||
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
||||
char *cmd;
|
||||
char *newpath;
|
||||
char *metachars = "\"\\!(){}#*?$&<>`"; /* More?? */
|
||||
char metacount = 0;
|
||||
char *src,*dst;
|
||||
|
||||
src=file;
|
||||
while(*src) {
|
||||
if(strchr(metachars,*src))
|
||||
metacount+=5;
|
||||
src++;
|
||||
}
|
||||
|
||||
if(metachars) {
|
||||
newpath = (char*)malloc(strlen(file) + metacount + 1);
|
||||
if(!newpath) {
|
||||
_ppi->log(E_FATAL,"ssc_script_open: malloc\n");
|
||||
}
|
||||
src=file;
|
||||
dst=newpath;
|
||||
|
||||
while(*src) {
|
||||
if(strchr(metachars,*src)) {
|
||||
*dst++='"';
|
||||
*dst++='\'';
|
||||
*dst++=*src++;
|
||||
*dst++='\'';
|
||||
*dst++='"';
|
||||
} else {
|
||||
*dst++=*src++;
|
||||
}
|
||||
}
|
||||
*dst='\0';
|
||||
} else {
|
||||
newpath = strdup(file); /* becuase it will be freed... */
|
||||
}
|
||||
|
||||
/* FIXME: is 64 enough? is there a better way to determine this? */
|
||||
cmd=(char *)malloc(strlen(_ssc_script_program) +
|
||||
strlen(file) +
|
||||
64);
|
||||
sprintf(cmd, "%s \"%s\" 0 %lu.%03lu \"%s\"",
|
||||
_ssc_script_program, newpath, (unsigned long) duration / 1000,
|
||||
(unsigned long)duration % 1000, (codec && *codec) ? codec : "*");
|
||||
_ppi->log(E_INF,"Executing %s\n",cmd);
|
||||
handle->fin = popen(cmd, "r");
|
||||
free(newpath);
|
||||
free(cmd); /* should really have in-place expanded the path */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int ssc_script_close(void *vp) {
|
||||
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
||||
|
||||
if(handle->fin) {
|
||||
pclose(handle->fin);
|
||||
handle->fin=NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int ssc_script_read(void *vp, char *buffer, int len) {
|
||||
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
||||
|
||||
return fread(buffer,1,len,handle->fin);
|
||||
}
|
||||
|
|
@ -41,6 +41,8 @@ struct tag_xmlstruct {
|
|||
XML_STREAMBUFFER *psb;
|
||||
};
|
||||
|
||||
extern PLUGIN_INPUT_FN *_ppi;
|
||||
|
||||
/* Forwards */
|
||||
void xml_get_stats(WS_CONNINFO *pwsc);
|
||||
void xml_set_config(WS_CONNINFO *pwsc);
|
||||
|
@ -62,7 +64,7 @@ void xml_write(XMLSTRUCT *pxml, char *fmt, ...) {
|
|||
if(pxml->psb) {
|
||||
xml_stream_write(pxml, buffer);
|
||||
} else {
|
||||
infn->ws_writefd(pxml->pwsc,"%s",buffer);
|
||||
_ppi->ws_writefd(pxml->pwsc,"%s",buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,14 +91,14 @@ XML_STREAMBUFFER *xml_stream_open(void) {
|
|||
|
||||
psb = (XML_STREAMBUFFER*) malloc(sizeof(XML_STREAMBUFFER));
|
||||
if(!psb) {
|
||||
infn->log(E_FATAL,"xml_stream_open: malloc\n");
|
||||
_ppi->log(E_FATAL,"xml_stream_open: malloc\n");
|
||||
}
|
||||
|
||||
psb->out_buffer = (unsigned char*) malloc(XML_STREAM_BLOCK);
|
||||
psb->in_buffer = (unsigned char*) malloc(XML_STREAM_BLOCK);
|
||||
|
||||
if((!psb->out_buffer) || (!psb->in_buffer)) {
|
||||
infn->log(E_FATAL,"xml_stream_open: malloc\n");
|
||||
_ppi->log(E_FATAL,"xml_stream_open: malloc\n");
|
||||
}
|
||||
|
||||
psb->strm.zalloc = Z_NULL;
|
||||
|
@ -134,9 +136,9 @@ int xml_stream_write(XMLSTRUCT *pxml, char *out) {
|
|||
while(!done) {
|
||||
result = deflate(&psb->strm, Z_NO_FLUSH);
|
||||
if(result != Z_OK) {
|
||||
infn->log(E_FATAL,"Error in zlib: %d\n",result);
|
||||
_ppi->log(E_FATAL,"Error in zlib: %d\n",result);
|
||||
}
|
||||
infn->ws_writebinary(pxml->pwsc,(char*)psb->out_buffer,
|
||||
_ppi->ws_writebinary(pxml->pwsc,(char*)psb->out_buffer,
|
||||
XML_STREAM_BLOCK-psb->strm.avail_out);
|
||||
if(psb->strm.avail_out != 0) {
|
||||
done=1;
|
||||
|
@ -163,14 +165,14 @@ int xml_stream_close(XMLSTRUCT *pxml) {
|
|||
psb->strm.next_in = psb->in_buffer;
|
||||
|
||||
deflate(&psb->strm,Z_FINISH);
|
||||
infn->ws_writebinary(pxml->pwsc,(char*)psb->out_buffer,
|
||||
_ppi->ws_writebinary(pxml->pwsc,(char*)psb->out_buffer,
|
||||
XML_STREAM_BLOCK - psb->strm.avail_out);
|
||||
|
||||
if(psb->strm.avail_out != 0)
|
||||
done=1;
|
||||
}
|
||||
|
||||
infn->log(E_DBG,"Done sending xml stream\n");
|
||||
_ppi->log(E_DBG,"Done sending xml stream\n");
|
||||
deflateEnd(&psb->strm);
|
||||
if(psb->out_buffer != NULL)
|
||||
free(psb->out_buffer);
|
||||
|
@ -196,7 +198,7 @@ XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
|||
|
||||
pxml=(XMLSTRUCT*)malloc(sizeof(XMLSTRUCT));
|
||||
if(!pxml) {
|
||||
infn->log(E_FATAL,"Malloc error\n");
|
||||
_ppi->log(E_FATAL,"Malloc error\n");
|
||||
}
|
||||
|
||||
memset(pxml,0,sizeof(XMLSTRUCT));
|
||||
|
@ -204,27 +206,27 @@ XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
|||
pxml->pwsc = pwsc;
|
||||
|
||||
/* should we compress output? */
|
||||
nogzip = infn->ws_getvar(pwsc,"nogzip");
|
||||
accept = infn->ws_getrequestheader(pwsc,"accept-encoding");
|
||||
nogzip = _ppi->ws_getvar(pwsc,"nogzip");
|
||||
accept = _ppi->ws_getrequestheader(pwsc,"accept-encoding");
|
||||
|
||||
if((!nogzip) && (accept) && (strcasestr(accept,"gzip"))) {
|
||||
infn->log(E_DBG,"Gzipping output\n");
|
||||
_ppi->log(E_DBG,"Gzipping output\n");
|
||||
pxml->psb = xml_stream_open();
|
||||
if(pxml->psb) {
|
||||
infn->ws_addresponseheader(pwsc,"Content-Encoding","gzip");
|
||||
infn->ws_addresponseheader(pwsc,"Vary","Accept-Encoding");
|
||||
infn->ws_addresponseheader(pwsc,"Connection","Close");
|
||||
_ppi->ws_addresponseheader(pwsc,"Content-Encoding","gzip");
|
||||
_ppi->ws_addresponseheader(pwsc,"Vary","Accept-Encoding");
|
||||
_ppi->ws_addresponseheader(pwsc,"Connection","Close");
|
||||
}
|
||||
}
|
||||
|
||||
/* the world would be a wonderful place without ie */
|
||||
infn->ws_addresponseheader(pwsc,"Cache-Control","no-cache");
|
||||
infn->ws_addresponseheader(pwsc,"Expires","-1");
|
||||
_ppi->ws_addresponseheader(pwsc,"Cache-Control","no-cache");
|
||||
_ppi->ws_addresponseheader(pwsc,"Expires","-1");
|
||||
|
||||
if(emit_header) {
|
||||
infn->ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
|
||||
infn->ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
|
||||
infn->ws_emitheaders(pwsc);
|
||||
_ppi->ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
|
||||
_ppi->ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
|
||||
_ppi->ws_emitheaders(pwsc);
|
||||
|
||||
|
||||
xml_write(pxml,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
|
||||
|
@ -263,7 +265,7 @@ void xml_pop(XMLSTRUCT *pxml) {
|
|||
|
||||
pstack=pxml->stack.next;
|
||||
if(!pstack) {
|
||||
infn->log(E_LOG,"xml_pop: tried to pop an empty stack\n");
|
||||
_ppi->log(E_LOG,"xml_pop: tried to pop an empty stack\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -310,7 +312,7 @@ void xml_deinit(XMLSTRUCT *pxml) {
|
|||
XMLSTACK *pstack;
|
||||
|
||||
if(pxml->stack.next) {
|
||||
infn->log(E_LOG,"xml_deinit: entries still on stack (%s)\n",
|
||||
_ppi->log(E_LOG,"xml_deinit: entries still on stack (%s)\n",
|
||||
pxml->stack.next->tag);
|
||||
}
|
||||
|
||||
|
|
161
src/ssc.c
161
src/ssc.c
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* $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>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <netinet/in.h> /* htons and friends */
|
||||
#include <dirent.h> /* why here? For osx 10.2, of course! */
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "conf.h"
|
||||
#include "daapd.h"
|
||||
#include "db-generic.h"
|
||||
#include "err.h"
|
||||
#include "mp3-scanner.h"
|
||||
#include "ssc.h"
|
||||
|
||||
/**
|
||||
* Check if the file specified by fname should be converted in
|
||||
* server to wav. Currently it does this by file extension, but
|
||||
* could in the future decided to transcode based on user agent.
|
||||
*
|
||||
* @param codectype codec type of the file we are checking for conversion
|
||||
* @returns 1 if should be converted. 0 if not
|
||||
*/
|
||||
int server_side_convert(char *codectype) {
|
||||
char *ssc_codectypes;
|
||||
|
||||
ssc_codectypes = conf_alloc_string("general","ssc_codectypes",
|
||||
"ogg,flac,wma,alac");
|
||||
|
||||
if ((!conf_isset("general","ssc_codectypes")) ||
|
||||
(!conf_isset("general","ssc_prog")) ||
|
||||
(!codectype)) {
|
||||
DPRINTF(E_DBG,L_SCAN,"Nope\n");
|
||||
free(ssc_codectypes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(strcasestr(ssc_codectypes, codectype)) {
|
||||
free(ssc_codectypes);
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(ssc_codectypes);
|
||||
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, unsigned long len_ms, char *codectype) {
|
||||
char *cmd;
|
||||
FILE *f;
|
||||
char *newpath;
|
||||
char *metachars = "\"\\!(){}#*?$&<>`"; /* More?? */
|
||||
char metacount = 0;
|
||||
char *src,*dst,*ssc_prog;
|
||||
|
||||
ssc_prog = conf_alloc_string("general","ssc_prog","");
|
||||
|
||||
if(ssc_prog[0] == '\0') { /* can't happen */
|
||||
free(ssc_prog);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
src=path;
|
||||
while(*src) {
|
||||
if(strchr(metachars,*src))
|
||||
metacount+=5;
|
||||
src++;
|
||||
}
|
||||
|
||||
if(metachars) {
|
||||
newpath = (char*)malloc(strlen(path) + metacount + 1);
|
||||
if(!newpath) {
|
||||
DPRINTF(E_FATAL,L_SCAN,"Malloc error.\n");
|
||||
}
|
||||
src=path;
|
||||
dst=newpath;
|
||||
|
||||
while(*src) {
|
||||
if(strchr(metachars,*src)) {
|
||||
*dst++='"';
|
||||
*dst++='\'';
|
||||
*dst++=*src++;
|
||||
*dst++='\'';
|
||||
*dst++='"';
|
||||
} else {
|
||||
*dst++=*src++;
|
||||
}
|
||||
}
|
||||
*dst='\0';
|
||||
} else {
|
||||
newpath = strdup(path); /* becuase it will be freed... */
|
||||
}
|
||||
|
||||
/* FIXME: is 64 enough? is there a better way to determine this? */
|
||||
cmd=(char *)malloc(strlen(ssc_prog) +
|
||||
strlen(path) +
|
||||
64);
|
||||
sprintf(cmd, "%s \"%s\" %ld %lu.%03lu \"%s\"",
|
||||
ssc_prog, newpath, (long)offset, len_ms / 1000,
|
||||
len_ms % 1000, (codectype && *codectype) ? codectype : "*");
|
||||
DPRINTF(E_INF,L_SCAN,"Executing %s\n",cmd);
|
||||
f = popen(cmd, "r");
|
||||
free(newpath);
|
||||
free(cmd); /* should really have in-place expanded the path */
|
||||
free(ssc_prog);
|
||||
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;
|
||||
}
|
||||
|
37
src/ssc.h
37
src/ssc.h
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* $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(char *codectype);
|
||||
extern char *server_side_convert_path(char *path);
|
||||
extern FILE *server_side_convert_open(char *path,
|
||||
off_t offset,
|
||||
unsigned long len_ms,
|
||||
char *codectype);
|
||||
extern void server_side_convert_close(FILE *f);
|
||||
|
||||
#endif /* _SCC_H_ */
|
||||
|
Loading…
Reference in New Issue