add ffmpeg-based transcoding

This commit is contained in:
Ron Pedde 2006-05-28 04:06:14 +00:00
parent da8f1a6f8f
commit 0f64686356
16 changed files with 708 additions and 413 deletions

View File

@ -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 \

View File

@ -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:
*

View File

@ -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

146
src/ff-plugins.h Normal file
View File

@ -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_

View File

@ -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"

View File

@ -53,7 +53,6 @@
#include "mp3-scanner.h"
#include "os.h"
#include "restart.h"
#include "ssc.h"
/*
* Typedefs

View File

@ -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) {

View File

@ -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

View File

@ -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..

View File

@ -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);
}

View File

@ -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

View File

@ -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);

206
src/plugins/ssc-script.c Normal file
View File

@ -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);
}

View File

@ -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
View File

@ -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;
}

View File

@ -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_ */