add ffmpeg-based transcoding
This commit is contained in:
parent
da8f1a6f8f
commit
0f64686356
|
@ -3,7 +3,7 @@
|
||||||
SUBDIRS=plugins
|
SUBDIRS=plugins
|
||||||
|
|
||||||
sbin_PROGRAMS = mt-daapd
|
sbin_PROGRAMS = mt-daapd
|
||||||
bin_PROGRAMS = wavstreamer mtd-update
|
bin_PROGRAMS = wavstreamer
|
||||||
|
|
||||||
if COND_REND_POSIX
|
if COND_REND_POSIX
|
||||||
PRENDSRC=mDNS.c mDNSClientAPI.h mDNSDebug.h mDNSPosix.c mDNSUNP.c \
|
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
|
GDBM=db-gdbm.c db-gdbm.h
|
||||||
endif
|
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
|
wavstreamer_SOURCES = wavstreamer.c
|
||||||
|
|
||||||
mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.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 \
|
webserver.h configfile.c configfile.h err.c err.h restart.c restart.h \
|
||||||
mp3-scanner.h mp3-scanner.c rend-unix.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 \
|
db-generic.c db-generic.h dispatch.c dispatch.h \
|
||||||
rxml.c rxml.h redblack.c redblack.h scan-mp3.c scan-mp4.c \
|
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 \
|
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-generic.h"
|
||||||
#include "db-sql.h"
|
#include "db-sql.h"
|
||||||
#include "restart.h"
|
#include "restart.h"
|
||||||
#include "ssc.h"
|
|
||||||
#include "smart-parser.h"
|
#include "smart-parser.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
#ifdef HAVE_LIBSQLITE
|
#ifdef HAVE_LIBSQLITE
|
||||||
#include "db-sql-sqlite2.h"
|
#include "db-sql-sqlite2.h"
|
||||||
|
@ -1354,7 +1354,7 @@ int db_sql_get_size(DBQUERYINFO *pinfo, SQL_ROW valarray) {
|
||||||
case queryTypeItems:
|
case queryTypeItems:
|
||||||
case queryTypePlaylistItems: /* essentially the same query */
|
case queryTypePlaylistItems: /* essentially the same query */
|
||||||
/* see if this is going to be transcoded */
|
/* 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:
|
/* 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 queryTypeItems:
|
||||||
case queryTypePlaylistItems: /* essentially the same query */
|
case queryTypePlaylistItems: /* essentially the same query */
|
||||||
/* see if this is going to be transcoded */
|
/* 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:
|
/* Items that get changed by transcode:
|
||||||
*
|
*
|
||||||
|
|
|
@ -42,8 +42,8 @@
|
||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
#include "mp3-scanner.h"
|
#include "mp3-scanner.h"
|
||||||
|
#include "plugin.h"
|
||||||
#include "webserver.h"
|
#include "webserver.h"
|
||||||
#include "ssc.h"
|
|
||||||
#include "dynamic-art.h"
|
#include "dynamic-art.h"
|
||||||
#include "restart.h"
|
#include "restart.h"
|
||||||
#include "daapd.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) {
|
void dispatch_stream_id(WS_CONNINFO *pwsc, int session, char *id) {
|
||||||
MP3FILE *pmp3;
|
MP3FILE *pmp3;
|
||||||
FILE *file_ptr;
|
|
||||||
int file_fd;
|
int file_fd;
|
||||||
int bytes_copied;
|
int bytes_copied;
|
||||||
off_t real_len;
|
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);
|
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
|
||||||
config_set_status(pwsc,session,NULL);
|
config_set_status(pwsc,session,NULL);
|
||||||
ws_returnerror(pwsc,404,"File Not Found");
|
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
|
* 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");
|
|
||||||
|
|
||||||
/*
|
|
||||||
if(pmp3->type)
|
|
||||||
ws_addresponseheader(pwsc,"Content-Type","audio/%s",
|
|
||||||
pmp3->type);
|
|
||||||
*/
|
|
||||||
// Also content-length -heade would be nice, but since
|
|
||||||
// we don't really know it here, so let's leave it out.
|
|
||||||
ws_addresponseheader(pwsc,"Connection","Close");
|
|
||||||
|
|
||||||
if(!offset)
|
|
||||||
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
|
||||||
else {
|
|
||||||
// This is actually against the protocol, since
|
|
||||||
// range MUST be explicit according to HTTP-standard
|
|
||||||
// Seems to work at least with iTunes.
|
|
||||||
ws_addresponseheader(pwsc,
|
|
||||||
"Content-Range","bytes %ld-*/*",
|
|
||||||
(long)offset);
|
|
||||||
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
ws_emitheaders(pwsc);
|
|
||||||
|
|
||||||
config_set_status(pwsc,session,
|
config_set_status(pwsc,session,
|
||||||
"Transcoding '%s' (id %d)",
|
"Transcoding '%s' (id %d)",
|
||||||
pmp3->title,pmp3->id);
|
pmp3->title,pmp3->id);
|
||||||
|
|
||||||
DPRINTF(E_LOG,L_WS,
|
DPRINTF(E_LOG,L_WS,
|
||||||
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
|
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
|
||||||
session,pmp3->fname, pwsc->hostname,(long)offset);
|
session,pmp3->fname, pwsc->hostname,(long)offset);
|
||||||
|
|
||||||
if(!offset)
|
bytes_copied = plugin_ssc_transcode(pwsc,pmp3->path,pmp3->codectype,
|
||||||
config.stats.songs_served++; /* FIXME: remove stat races */
|
pmp3->song_length,offset);
|
||||||
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);
|
config_set_status(pwsc,session,NULL);
|
||||||
db_dispose_item(pmp3);
|
db_dispose_item(pmp3);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/**********************
|
/**********************
|
||||||
* stream file normally
|
* 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 "mp3-scanner.h"
|
||||||
#include "webserver.h"
|
#include "webserver.h"
|
||||||
#include "restart.h"
|
#include "restart.h"
|
||||||
#include "ssc.h"
|
|
||||||
#include "dynamic-art.h"
|
#include "dynamic-art.h"
|
||||||
#include "db-generic.h"
|
#include "db-generic.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
|
@ -53,7 +53,6 @@
|
||||||
#include "mp3-scanner.h"
|
#include "mp3-scanner.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "restart.h"
|
#include "restart.h"
|
||||||
#include "ssc.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Typedefs
|
* Typedefs
|
||||||
|
|
250
src/plugin.c
250
src/plugin.c
|
@ -47,12 +47,8 @@
|
||||||
|
|
||||||
typedef struct tag_pluginentry {
|
typedef struct tag_pluginentry {
|
||||||
void *phandle;
|
void *phandle;
|
||||||
int type;
|
|
||||||
char *versionstring;
|
|
||||||
regex_t regex;
|
regex_t regex;
|
||||||
PLUGIN_OUTPUT_FN *output_fns;
|
PLUGIN_INFO *pinfo;
|
||||||
PLUGIN_EVENT_FN *event_fns;
|
|
||||||
PLUGIN_REND_INFO *rend_info;
|
|
||||||
struct tag_pluginentry *next;
|
struct tag_pluginentry *next;
|
||||||
} PLUGIN_ENTRY;
|
} PLUGIN_ENTRY;
|
||||||
|
|
||||||
|
@ -60,7 +56,7 @@ typedef struct tag_pluginentry {
|
||||||
static pthread_key_t _plugin_lock_key;
|
static pthread_key_t _plugin_lock_key;
|
||||||
static PLUGIN_ENTRY _plugin_list;
|
static PLUGIN_ENTRY _plugin_list;
|
||||||
static int _plugin_initialized = 0;
|
static int _plugin_initialized = 0;
|
||||||
|
static char *_plugin_ssc_codecs = NULL;
|
||||||
static pthread_rwlock_t _plugin_lock;
|
static pthread_rwlock_t _plugin_lock;
|
||||||
|
|
||||||
static char* _plugin_error_list[] = {
|
static char* _plugin_error_list[] = {
|
||||||
|
@ -75,6 +71,7 @@ void _plugin_writelock(void);
|
||||||
void _plugin_unlock(void);
|
void _plugin_unlock(void);
|
||||||
int _plugin_error(char **pe, int error, ...);
|
int _plugin_error(char **pe, int error, ...);
|
||||||
void _plugin_free(int *pi);
|
void _plugin_free(int *pi);
|
||||||
|
void _plugin_recalc_codecs(void);
|
||||||
|
|
||||||
/* webserver helpers */
|
/* webserver helpers */
|
||||||
char *pi_ws_uri(WS_CONNINFO *pwsc);
|
char *pi_ws_uri(WS_CONNINFO *pwsc);
|
||||||
|
@ -270,6 +267,53 @@ int _plugin_error(char **pe, int error, ...) {
|
||||||
return 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.
|
* load a specified plugin.
|
||||||
*
|
*
|
||||||
|
@ -281,7 +325,7 @@ int _plugin_error(char **pe, int error, ...) {
|
||||||
int plugin_load(char **pe, char *path) {
|
int plugin_load(char **pe, char *path) {
|
||||||
PLUGIN_ENTRY *ppi;
|
PLUGIN_ENTRY *ppi;
|
||||||
void *phandle;
|
void *phandle;
|
||||||
PLUGIN_INFO *(*info_func)(void);
|
PLUGIN_INFO *(*info_func)(PLUGIN_INPUT_FN *);
|
||||||
PLUGIN_INFO *pinfo;
|
PLUGIN_INFO *pinfo;
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_PLUG,"Attempting to load plugin %s\n",path);
|
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;
|
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) {
|
if(info_func == NULL) {
|
||||||
DPRINTF(E_INF,L_PLUG,"Couldn't get info_func for %s\n",path);
|
DPRINTF(E_INF,L_PLUG,"Couldn't get info_func for %s\n",path);
|
||||||
|
os_unload(phandle);
|
||||||
|
free(ppi);
|
||||||
return PLUGIN_E_BADFUNCS;
|
return PLUGIN_E_BADFUNCS;
|
||||||
}
|
}
|
||||||
|
|
||||||
pinfo = info_func();
|
pinfo = info_func(&pi);
|
||||||
|
ppi->pinfo = pinfo;
|
||||||
|
|
||||||
ppi->type = pinfo->type;
|
if(!pinfo) {
|
||||||
ppi->versionstring = pinfo->server;
|
if(pe) *pe = strdup("plugin declined to load");
|
||||||
if(ppi->type & PLUGIN_OUTPUT) {
|
os_unload(phandle);
|
||||||
|
free(ppi);
|
||||||
|
return PLUGIN_E_NOLOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pinfo->type & PLUGIN_OUTPUT) {
|
||||||
/* build the regex */
|
/* build the regex */
|
||||||
if(regcomp(&ppi->regex,pinfo->url,REG_EXTENDED | REG_NOSUB)) {
|
if(regcomp(&ppi->regex,pinfo->url,REG_EXTENDED | REG_NOSUB)) {
|
||||||
DPRINTF(E_LOG,L_PLUG,"Bad regex in %s: %s\n",path,pinfo->url);
|
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);
|
DPRINTF(E_INF,L_PLUG,"Loaded plugin %s (%s)\n",path,pinfo->server);
|
||||||
pinfo->pi = (void*)π
|
|
||||||
|
|
||||||
_plugin_writelock();
|
_plugin_writelock();
|
||||||
|
|
||||||
if(!_plugin_initialized) {
|
if(!_plugin_initialized) {
|
||||||
_plugin_initialized = 1;
|
_plugin_initialized = 1;
|
||||||
memset((void*)&_plugin_list,0,sizeof(_plugin_list));
|
memset((void*)&_plugin_list,0,sizeof(_plugin_list));
|
||||||
|
@ -331,6 +380,7 @@ int plugin_load(char **pe, char *path) {
|
||||||
|
|
||||||
_plugin_unlock();
|
_plugin_unlock();
|
||||||
|
|
||||||
|
_plugin_recalc_codecs();
|
||||||
return PLUGIN_E_SUCCESS;
|
return PLUGIN_E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,7 +398,7 @@ int plugin_url_candispatch(WS_CONNINFO *pwsc) {
|
||||||
_plugin_readlock();
|
_plugin_readlock();
|
||||||
ppi = _plugin_list.next;
|
ppi = _plugin_list.next;
|
||||||
while(ppi) {
|
while(ppi) {
|
||||||
if(ppi->type & PLUGIN_OUTPUT) {
|
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
|
||||||
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
|
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
|
||||||
/* we have a winner */
|
/* we have a winner */
|
||||||
_plugin_unlock();
|
_plugin_unlock();
|
||||||
|
@ -375,14 +425,14 @@ void plugin_url_handle(WS_CONNINFO *pwsc) {
|
||||||
_plugin_readlock();
|
_plugin_readlock();
|
||||||
ppi = _plugin_list.next;
|
ppi = _plugin_list.next;
|
||||||
while(ppi) {
|
while(ppi) {
|
||||||
if(ppi->type & PLUGIN_OUTPUT) {
|
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
|
||||||
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
|
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
|
||||||
/* we have a winner */
|
/* we have a winner */
|
||||||
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", pwsc->uri,
|
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 */
|
/* 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);
|
disp_fn(pwsc);
|
||||||
_plugin_unlock();
|
_plugin_unlock();
|
||||||
return;
|
return;
|
||||||
|
@ -407,7 +457,6 @@ int plugin_rend_register(char *name, int port, char *iface, char *txt) {
|
||||||
char *supplied_txt;
|
char *supplied_txt;
|
||||||
char *new_name;
|
char *new_name;
|
||||||
char *ver;
|
char *ver;
|
||||||
char *slash;
|
|
||||||
int name_len;
|
int name_len;
|
||||||
|
|
||||||
|
|
||||||
|
@ -415,9 +464,9 @@ int plugin_rend_register(char *name, int port, char *iface, char *txt) {
|
||||||
ppi = _plugin_list.next;
|
ppi = _plugin_list.next;
|
||||||
|
|
||||||
while(ppi) {
|
while(ppi) {
|
||||||
DPRINTF(E_DBG,L_PLUG,"Checking %s\n",ppi->versionstring);
|
DPRINTF(E_DBG,L_PLUG,"Checking %s\n",ppi->pinfo->server);
|
||||||
if(ppi->rend_info) {
|
if(ppi->pinfo->rend_info) {
|
||||||
pri = ppi->rend_info;
|
pri = ppi->pinfo->rend_info;
|
||||||
while(pri->type) {
|
while(pri->type) {
|
||||||
supplied_txt = pri->txt;
|
supplied_txt = pri->txt;
|
||||||
if(!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);
|
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);
|
new_name=(char*)malloc(name_len);
|
||||||
if(!new_name)
|
if(!new_name)
|
||||||
DPRINTF(E_FATAL,L_PLUG,"plugin_rend_register: malloc");
|
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);
|
memset(new_name,0,name_len);
|
||||||
|
|
||||||
if(conf_get_int("plugins","mangle_rendezvous",1)) {
|
if(conf_get_int("plugins","mangle_rendezvous",1)) {
|
||||||
ver = strdup(ppi->versionstring);
|
ver = strdup(ppi->pinfo->server);
|
||||||
if(strchr(ver,'/')) {
|
if(strchr(ver,'/')) {
|
||||||
*strchr(ver,'/') = '\0';
|
*strchr(ver,'/') = '\0';
|
||||||
}
|
}
|
||||||
|
@ -472,14 +521,14 @@ int plugin_auth_handle(WS_CONNINFO *pwsc, char *username, char *pw) {
|
||||||
_plugin_readlock();
|
_plugin_readlock();
|
||||||
ppi = _plugin_list.next;
|
ppi = _plugin_list.next;
|
||||||
while(ppi) {
|
while(ppi) {
|
||||||
if(ppi->type & PLUGIN_OUTPUT) {
|
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
|
||||||
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
|
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
|
||||||
/* we have a winner */
|
/* we have a winner */
|
||||||
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", pwsc->uri,
|
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 */
|
/* 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) {
|
if(auth_fn) {
|
||||||
result=auth_fn(pwsc,username,pw);
|
result=auth_fn(pwsc,username,pw);
|
||||||
_plugin_unlock();
|
_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) {
|
void plugin_event_dispatch(int event_id, int intval, void *vp, int len) {
|
||||||
PLUGIN_ENTRY *ppi;
|
PLUGIN_ENTRY *ppi;
|
||||||
|
|
||||||
fprintf(stderr,"entering plugin_event_dispatch\n");
|
_plugin_readlock();
|
||||||
|
|
||||||
// _plugin_readlock();
|
|
||||||
ppi = _plugin_list.next;
|
ppi = _plugin_list.next;
|
||||||
while(ppi) {
|
while(ppi) {
|
||||||
fprintf(stderr,"Checking %s\n",ppi->versionstring);
|
fprintf(stderr,"Checking %s\n",ppi->pinfo->server);
|
||||||
if(ppi->type & PLUGIN_EVENT) {
|
if(ppi->pinfo->type & PLUGIN_EVENT) {
|
||||||
/* DPRINTF(E_DBG,L_PLUG,"Dispatching event %d to %s\n",
|
/* DPRINTF(E_DBG,L_PLUG,"Dispatching event %d to %s\n",
|
||||||
event_id,ppi->versionstring); */
|
event_id,ppi->versionstring); */
|
||||||
|
|
||||||
if((ppi->event_fns) && (ppi->event_fns->handler)) {
|
if((ppi->pinfo->event_fns) && (ppi->pinfo->event_fns->handler)) {
|
||||||
ppi->event_fns->handler(event_id, intval, vp, len);
|
ppi->pinfo->event_fns->handler(event_id, intval, vp, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ppi=ppi->next;
|
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
|
/* 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;
|
pqi->playlist_id = pinfo->playlist_id;
|
||||||
result = db_enum_start(pe, pqi);
|
result = db_enum_start(pe, pqi);
|
||||||
pinfo->totalcount = pqi->specifiedtotalcount;
|
pinfo->totalcount = pqi->specifiedtotalcount;
|
||||||
|
|
||||||
|
return DB_E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pi_db_enum_fetch_row(char **pe, char ***row, DB_QUERY *pinfo) {
|
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 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);
|
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_SUCCESS 0
|
||||||
#define PLUGIN_E_NOLOAD 1
|
#define PLUGIN_E_NOLOAD 1
|
||||||
#define PLUGIN_E_BADFUNCS 2
|
#define PLUGIN_E_BADFUNCS 2
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#
|
#
|
||||||
rspdir = ${pkgdatadir}/plugins
|
rspdir = ${pkgdatadir}/plugins
|
||||||
ssc_ffmpegdir = ${pkgdatadir}/plugins
|
ssc_ffmpegdir = ${pkgdatadir}/plugins
|
||||||
|
ssc_scriptdir = ${pkgdatadir}/plugins
|
||||||
|
|
||||||
rsp_LTLIBRARIES=rsp.la
|
rsp_LTLIBRARIES=rsp.la
|
||||||
rsp_la_LDFLAGS=-module -avoid-version
|
rsp_la_LDFLAGS=-module -avoid-version
|
||||||
|
@ -13,7 +14,11 @@ ssc_ffmpeg_la_LDFLAGS=-module -avoid-version
|
||||||
ssc_ffmpeg_la_SOURCES=ssc-ffmpeg.c
|
ssc_ffmpeg_la_SOURCES=ssc-ffmpeg.c
|
||||||
endif
|
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..
|
AM_CFLAGS = -I..
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ typedef struct tag_rsp_privinfo {
|
||||||
} PRIVINFO;
|
} PRIVINFO;
|
||||||
|
|
||||||
/* Forwards */
|
/* Forwards */
|
||||||
PLUGIN_INFO *plugin_info(void);
|
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *);
|
||||||
void plugin_handler(WS_CONNINFO *pwsc);
|
void plugin_handler(WS_CONNINFO *pwsc);
|
||||||
int plugin_auth(WS_CONNINFO *pwsc, char *username, char *pw);
|
int plugin_auth(WS_CONNINFO *pwsc, char *username, char *pw);
|
||||||
void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi);
|
void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi);
|
||||||
|
@ -39,6 +39,8 @@ PLUGIN_REND_INFO _pri[] = {
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PLUGIN_INPUT_FN *_ppi;
|
||||||
|
|
||||||
PLUGIN_INFO _pi = {
|
PLUGIN_INFO _pi = {
|
||||||
PLUGIN_VERSION, /* version */
|
PLUGIN_VERSION, /* version */
|
||||||
PLUGIN_OUTPUT, /* type */
|
PLUGIN_OUTPUT, /* type */
|
||||||
|
@ -47,7 +49,6 @@ PLUGIN_INFO _pi = {
|
||||||
&_pofn, /* output fns */
|
&_pofn, /* output fns */
|
||||||
NULL, /* event fns */
|
NULL, /* event fns */
|
||||||
NULL, /* transcode fns */
|
NULL, /* transcode fns */
|
||||||
NULL, /* ff functions */
|
|
||||||
_pri, /* rend info */
|
_pri, /* rend info */
|
||||||
NULL /* transcode info */
|
NULL /* transcode info */
|
||||||
};
|
};
|
||||||
|
@ -145,7 +146,8 @@ FIELDSPEC rsp_fields[] = {
|
||||||
/**
|
/**
|
||||||
* return info about this plugin module
|
* return info about this plugin module
|
||||||
*/
|
*/
|
||||||
PLUGIN_INFO *plugin_info(void) {
|
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *ppi) {
|
||||||
|
_ppi = ppi;
|
||||||
return &_pi;
|
return &_pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +170,7 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
||||||
int index, part;
|
int index, part;
|
||||||
int found;
|
int found;
|
||||||
|
|
||||||
string = infn->ws_uri(pwsc);
|
string = _ppi->ws_uri(pwsc);
|
||||||
string++;
|
string++;
|
||||||
|
|
||||||
ppi = (PRIVINFO *)malloc(sizeof(PRIVINFO));
|
ppi = (PRIVINFO *)malloc(sizeof(PRIVINFO));
|
||||||
|
@ -177,27 +179,27 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ppi) {
|
if(!ppi) {
|
||||||
infn->ws_returnerror(pwsc,500,"Malloc error in plugin_handler");
|
_ppi->ws_returnerror(pwsc,500,"Malloc error in plugin_handler");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset((void*)&ppi->dq,0,sizeof(DB_QUERY));
|
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))) {
|
while((ppi->uri_count < 10) && (token=strtok_r(string,"/",&save))) {
|
||||||
string=NULL;
|
string=NULL;
|
||||||
ppi->uri_sections[ppi->uri_count++] = token;
|
ppi->uri_sections[ppi->uri_count++] = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
elements = sizeof(rsp_uri_map) / sizeof(PLUGIN_RESPONSE);
|
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;
|
index = 0;
|
||||||
found = 0;
|
found = 0;
|
||||||
|
|
||||||
while((!found) && (index < elements)) {
|
while((!found) && (index < elements)) {
|
||||||
/* test this set */
|
/* test this set */
|
||||||
infn->log(E_DBG,"Checking reponse %d\n",index);
|
_ppi->log(E_DBG,"Checking reponse %d\n",index);
|
||||||
part=0;
|
part=0;
|
||||||
while(part < 10) {
|
while(part < 10) {
|
||||||
if((rsp_uri_map[index].uri[part]) && (!ppi->uri_sections[part]))
|
if((rsp_uri_map[index].uri[part]) && (!ppi->uri_sections[part]))
|
||||||
|
@ -216,7 +218,7 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
||||||
|
|
||||||
if(part == 10) {
|
if(part == 10) {
|
||||||
found = 1;
|
found = 1;
|
||||||
infn->log(E_DBG,"Found it! Index: %d\n",index);
|
_ppi->log(E_DBG,"Found it! Index: %d\n",index);
|
||||||
} else {
|
} else {
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
@ -224,13 +226,13 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
||||||
|
|
||||||
if(found) {
|
if(found) {
|
||||||
rsp_uri_map[index].dispatch(pwsc, ppi);
|
rsp_uri_map[index].dispatch(pwsc, ppi);
|
||||||
infn->ws_close(pwsc);
|
_ppi->ws_close(pwsc);
|
||||||
free(ppi);
|
free(ppi);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp_error(pwsc, ppi, 1, "Bad path");
|
rsp_error(pwsc, ppi, 1, "Bad path");
|
||||||
infn->ws_close(pwsc);
|
_ppi->ws_close(pwsc);
|
||||||
free(ppi);
|
free(ppi);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -243,7 +245,7 @@ void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
char servername[256];
|
char servername[256];
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
infn->log(E_DBG,"Starting rsp_info\n");
|
_ppi->log(E_DBG,"Starting rsp_info\n");
|
||||||
|
|
||||||
pxml = xml_init(pwsc,1);
|
pxml = xml_init(pwsc,1);
|
||||||
|
|
||||||
|
@ -257,13 +259,13 @@ void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
|
|
||||||
/* info block */
|
/* info block */
|
||||||
xml_push(pxml,"info");
|
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,"rsp-version",RSP_VERSION);
|
||||||
|
|
||||||
xml_output(pxml,"server-version",infn->server_ver());
|
xml_output(pxml,"server-version",_ppi->server_ver());
|
||||||
|
|
||||||
size = sizeof(servername);
|
size = sizeof(servername);
|
||||||
infn->server_name(servername,&size);
|
_ppi->server_name(servername,&size);
|
||||||
xml_output(pxml,"name",servername);
|
xml_output(pxml,"name",servername);
|
||||||
xml_pop(pxml); /* info */
|
xml_pop(pxml); /* info */
|
||||||
|
|
||||||
|
@ -285,9 +287,9 @@ void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
|
|
||||||
ppi->dq.query_type = QUERY_TYPE_PLAYLISTS;
|
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);
|
rsp_error(pwsc, ppi, err | E_DB, pe);
|
||||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +305,7 @@ void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
|
|
||||||
xml_push(pxml,"playlists");
|
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");
|
xml_push(pxml,"playlist");
|
||||||
rowindex=0;
|
rowindex=0;
|
||||||
while(rsp_playlist_fields[rowindex].name) {
|
while(rsp_playlist_fields[rowindex].name) {
|
||||||
|
@ -316,8 +318,8 @@ void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
xml_pop(pxml); /* playlist */
|
xml_pop(pxml); /* playlist */
|
||||||
}
|
}
|
||||||
|
|
||||||
infn->db_enum_end(NULL);
|
_ppi->db_enum_end(NULL);
|
||||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||||
|
|
||||||
xml_pop(pxml); /* playlists */
|
xml_pop(pxml); /* playlists */
|
||||||
xml_pop(pxml); /* response */
|
xml_pop(pxml); /* response */
|
||||||
|
@ -343,7 +345,7 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
// char *user_agent;
|
// char *user_agent;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
user_agent = infn->ws_getrequestheader(pwsc,"user-agent");
|
user_agent = _ppi->ws_getrequestheader(pwsc,"user-agent");
|
||||||
if(user_agent) {
|
if(user_agent) {
|
||||||
if(strncmp(user_agent,"iTunes",6)==0) {
|
if(strncmp(user_agent,"iTunes",6)==0) {
|
||||||
trancode_codecs = "wma,ogg,flac,mpc";
|
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;
|
ppi->dq.filter_type = FILTER_TYPE_FIREFLY;
|
||||||
|
|
||||||
if(infn->ws_getvar(pwsc,"offset")) {
|
if(_ppi->ws_getvar(pwsc,"offset")) {
|
||||||
ppi->dq.offset = atoi(infn->ws_getvar(pwsc,"offset"));
|
ppi->dq.offset = atoi(_ppi->ws_getvar(pwsc,"offset"));
|
||||||
}
|
}
|
||||||
if(infn->ws_getvar(pwsc,"limit")) {
|
if(_ppi->ws_getvar(pwsc,"limit")) {
|
||||||
ppi->dq.limit = atoi(infn->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;
|
type = F_FULL;
|
||||||
|
|
||||||
if(browse_type) {
|
if(browse_type) {
|
||||||
|
@ -378,9 +380,9 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
ppi->dq.query_type = QUERY_TYPE_ITEMS;
|
ppi->dq.query_type = QUERY_TYPE_ITEMS;
|
||||||
ppi->dq.playlist_id = atoi(ppi->uri_sections[2]);
|
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);
|
rsp_error(pwsc, ppi, err | E_DB, pe);
|
||||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||||
free(pe);
|
free(pe);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -395,7 +397,7 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
returned = ppi->dq.totalcount - ppi->dq.offset;
|
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,"response");
|
||||||
xml_push(pxml,"status");
|
xml_push(pxml,"status");
|
||||||
|
@ -407,7 +409,7 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
|
|
||||||
xml_push(pxml,"items");
|
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");
|
xml_push(pxml,"item");
|
||||||
rowindex=0;
|
rowindex=0;
|
||||||
transcode = 0;
|
transcode = 0;
|
||||||
|
@ -453,8 +455,8 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
xml_pop(pxml); /* item */
|
xml_pop(pxml); /* item */
|
||||||
}
|
}
|
||||||
|
|
||||||
infn->db_enum_end(NULL);
|
_ppi->db_enum_end(NULL);
|
||||||
infn->conf_dispose_string(transcode_codecs);
|
_ppi->conf_dispose_string(transcode_codecs);
|
||||||
|
|
||||||
xml_pop(pxml); /* items */
|
xml_pop(pxml); /* items */
|
||||||
xml_pop(pxml); /* response */
|
xml_pop(pxml); /* response */
|
||||||
|
@ -471,22 +473,22 @@ void rsp_browse(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
/* this might fail if an unsupported browse type */
|
/* this might fail if an unsupported browse type */
|
||||||
ppi->dq.query_type = QUERY_TYPE_DISTINCT;
|
ppi->dq.query_type = QUERY_TYPE_DISTINCT;
|
||||||
ppi->dq.distinct_field = ppi->uri_sections[3];
|
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;
|
ppi->dq.filter_type = FILTER_TYPE_FIREFLY;
|
||||||
|
|
||||||
if(infn->ws_getvar(pwsc,"offset")) {
|
if(_ppi->ws_getvar(pwsc,"offset")) {
|
||||||
ppi->dq.offset = atoi(infn->ws_getvar(pwsc,"offset"));
|
ppi->dq.offset = atoi(_ppi->ws_getvar(pwsc,"offset"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(infn->ws_getvar(pwsc,"limit")) {
|
if(_ppi->ws_getvar(pwsc,"limit")) {
|
||||||
ppi->dq.limit = atoi(infn->ws_getvar(pwsc,"limit"));
|
ppi->dq.limit = atoi(_ppi->ws_getvar(pwsc,"limit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ppi->dq.playlist_id = atoi(ppi->uri_sections[2]);
|
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);
|
rsp_error(pwsc, ppi, err | E_DB, pe);
|
||||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,12 +512,12 @@ void rsp_browse(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
|
|
||||||
xml_push(pxml,"items");
|
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]);
|
xml_output(pxml,"item",row[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
infn->db_enum_end(NULL);
|
_ppi->db_enum_end(NULL);
|
||||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||||
|
|
||||||
xml_pop(pxml); /* items */
|
xml_pop(pxml); /* items */
|
||||||
xml_pop(pxml); /* response */
|
xml_pop(pxml); /* response */
|
||||||
|
@ -523,7 +525,7 @@ void rsp_browse(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void rsp_stream(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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,6 +542,6 @@ void rsp_error(WS_CONNINFO *pwsc, PRIVINFO *ppi, int eno, char *estr) {
|
||||||
xml_pop(pxml); /* status */
|
xml_pop(pxml); /* status */
|
||||||
xml_pop(pxml); /* response */
|
xml_pop(pxml); /* response */
|
||||||
xml_deinit(pxml);
|
xml_deinit(pxml);
|
||||||
infn->ws_close(pwsc);
|
_ppi->ws_close(pwsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#define RSP_VERSION "1.0"
|
#define RSP_VERSION "1.0"
|
||||||
|
|
||||||
extern PLUGIN_INFO _pi;
|
extern PLUGIN_INFO _pi;
|
||||||
#define infn ((PLUGIN_INPUT_FN *)(_pi.pi))
|
|
||||||
|
|
||||||
#ifndef TRUE
|
#ifndef TRUE
|
||||||
# define TRUE 1
|
# define TRUE 1
|
||||||
|
|
|
@ -40,6 +40,8 @@ typedef struct tag_ssc_handle {
|
||||||
int buf_remainder_len;
|
int buf_remainder_len;
|
||||||
int first_frame;
|
int first_frame;
|
||||||
|
|
||||||
|
int duration;
|
||||||
|
|
||||||
int total_decoded;
|
int total_decoded;
|
||||||
int total_written;
|
int total_written;
|
||||||
|
|
||||||
|
@ -64,19 +66,27 @@ typedef struct tag_ssc_handle {
|
||||||
#define SSC_FFMPEG_E_FILEOPEN 3
|
#define SSC_FFMPEG_E_FILEOPEN 3
|
||||||
#define SSC_FFMPEG_E_NOSTREAM 4
|
#define SSC_FFMPEG_E_NOSTREAM 4
|
||||||
#define SSC_FFMPEG_E_NOAUDIO 5
|
#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 */
|
/* Forwards */
|
||||||
void *ssc_ffmpeg_init(void);
|
void *ssc_ffmpeg_init(void);
|
||||||
void ssc_ffmpeg_deinit(void *pv);
|
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_close(void *pv);
|
||||||
int ssc_ffmpeg_read(void *pv, char *buffer, int len);
|
int ssc_ffmpeg_read(void *pv, char *buffer, int len);
|
||||||
|
char *ssc_ffmpeg_error(void *pv);
|
||||||
|
|
||||||
PLUGIN_INFO *plugin_info(void);
|
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN*);
|
||||||
|
|
||||||
#define infn ((PLUGIN_INPUT_FN *)(_pi.pi))
|
|
||||||
|
|
||||||
/* Globals */
|
/* Globals */
|
||||||
PLUGIN_TRANSCODE_FN _ptfn = {
|
PLUGIN_TRANSCODE_FN _ptfn = {
|
||||||
|
@ -84,9 +94,12 @@ PLUGIN_TRANSCODE_FN _ptfn = {
|
||||||
ssc_ffmpeg_deinit,
|
ssc_ffmpeg_deinit,
|
||||||
ssc_ffmpeg_open,
|
ssc_ffmpeg_open,
|
||||||
ssc_ffmpeg_close,
|
ssc_ffmpeg_close,
|
||||||
ssc_ffmpeg_read
|
ssc_ffmpeg_read,
|
||||||
|
ssc_ffmpeg_error
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PLUGIN_INPUT_FN *_ppi;
|
||||||
|
|
||||||
PLUGIN_INFO _pi = {
|
PLUGIN_INFO _pi = {
|
||||||
PLUGIN_VERSION, /* version */
|
PLUGIN_VERSION, /* version */
|
||||||
PLUGIN_TRANSCODE, /* type */
|
PLUGIN_TRANSCODE, /* type */
|
||||||
|
@ -95,12 +108,18 @@ PLUGIN_INFO _pi = {
|
||||||
NULL, /* output fns */
|
NULL, /* output fns */
|
||||||
NULL, /* event fns */
|
NULL, /* event fns */
|
||||||
&_ptfn, /* fns */
|
&_ptfn, /* fns */
|
||||||
NULL, /* functions exported by ff */
|
|
||||||
NULL, /* rend info */
|
NULL, /* rend info */
|
||||||
"flac,alac,ogg,wma" /* codeclist */
|
"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();
|
av_register_all();
|
||||||
|
|
||||||
return &_pi;
|
return &_pi;
|
||||||
|
@ -127,7 +146,7 @@ void ssc_ffmpeg_deinit(void *vp) {
|
||||||
return;
|
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;
|
int i;
|
||||||
enum CodecID id=CODEC_ID_FLAC;
|
enum CodecID id=CODEC_ID_FLAC;
|
||||||
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
||||||
|
@ -135,10 +154,11 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||||
if(!handle)
|
if(!handle)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
handle->duration = duration;
|
||||||
handle->first_frame = 1;
|
handle->first_frame = 1;
|
||||||
handle->raw=0;
|
handle->raw=0;
|
||||||
|
|
||||||
infn->log(E_DBG,"opening %s\n",file);
|
_ppi->log(E_DBG,"opening %s\n",file);
|
||||||
|
|
||||||
if(strcasecmp(codec,"flac") == 0) {
|
if(strcasecmp(codec,"flac") == 0) {
|
||||||
handle->raw=1;
|
handle->raw=1;
|
||||||
|
@ -146,6 +166,7 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(handle->raw) {
|
if(handle->raw) {
|
||||||
|
_ppi->log(E_DBG,"opening file raw\n");
|
||||||
handle->pCodec = avcodec_find_decoder(id);
|
handle->pCodec = avcodec_find_decoder(id);
|
||||||
if(!handle->pCodec) {
|
if(!handle->pCodec) {
|
||||||
handle->errno = SSC_FFMPEG_E_BADCODEC;
|
handle->errno = SSC_FFMPEG_E_BADCODEC;
|
||||||
|
@ -167,6 +188,7 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ppi->log(E_DBG,"opening file with format\n");
|
||||||
if(av_open_input_file(&handle->pFmtCtx,file,handle->pFormat,0,NULL) < 0) {
|
if(av_open_input_file(&handle->pFmtCtx,file,handle->pFormat,0,NULL) < 0) {
|
||||||
handle->errno = SSC_FFMPEG_E_FILEOPEN;
|
handle->errno = SSC_FFMPEG_E_FILEOPEN;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -178,7 +200,7 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_format(handle->pFmtCtx,0,file,FALSE);
|
// dump_format(handle->pFmtCtx,0,file,FALSE);
|
||||||
|
|
||||||
handle->audio_stream = -1;
|
handle->audio_stream = -1;
|
||||||
for(i=0; i < handle->pFmtCtx->nb_streams; i++) {
|
for(i=0; i < handle->pFmtCtx->nb_streams; i++) {
|
||||||
|
@ -226,8 +248,10 @@ int ssc_ffmpeg_close(void *vp) {
|
||||||
if(handle->pFrame)
|
if(handle->pFrame)
|
||||||
av_free(handle->pFrame);
|
av_free(handle->pFrame);
|
||||||
|
|
||||||
|
if(handle->raw) {
|
||||||
if(handle->pCodecCtx)
|
if(handle->pCodecCtx)
|
||||||
avcodec_close(handle->pCodecCtx);
|
avcodec_close(handle->pCodecCtx);
|
||||||
|
}
|
||||||
|
|
||||||
if(handle->pFmtCtx)
|
if(handle->pFmtCtx)
|
||||||
av_close_input_file(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) &&
|
handle->swab = (bits_per_sample == 16) &&
|
||||||
(memcmp((void*)&test1,test2,2) == 0);
|
(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;
|
byte_rate = sample_rate * channels * bits_per_sample / 8;
|
||||||
block_align = channels * bits_per_sample / 8;
|
block_align = channels * bits_per_sample / 8;
|
||||||
|
|
||||||
infn->log(E_DBG,"Channels.......: %d\n",channels);
|
_ppi->log(E_DBG,"Channels.......: %d\n",channels);
|
||||||
infn->log(E_DBG,"Sample rate....: %d\n",sample_rate);
|
_ppi->log(E_DBG,"Sample rate....: %d\n",sample_rate);
|
||||||
infn->log(E_DBG,"Bits/Sample....: %d\n",bits_per_sample);
|
_ppi->log(E_DBG,"Bits/Sample....: %d\n",bits_per_sample);
|
||||||
infn->log(E_DBG,"Swab...........: %d\n",handle->swab);
|
_ppi->log(E_DBG,"Swab...........: %d\n",handle->swab);
|
||||||
|
|
||||||
memcpy(&handle->wav_header[0],"RIFF",4);
|
memcpy(&handle->wav_header[0],"RIFF",4);
|
||||||
_ssc_ffmpeg_le32(&handle->wav_header[4],36 + data_len);
|
_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;
|
XML_STREAMBUFFER *psb;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern PLUGIN_INPUT_FN *_ppi;
|
||||||
|
|
||||||
/* Forwards */
|
/* Forwards */
|
||||||
void xml_get_stats(WS_CONNINFO *pwsc);
|
void xml_get_stats(WS_CONNINFO *pwsc);
|
||||||
void xml_set_config(WS_CONNINFO *pwsc);
|
void xml_set_config(WS_CONNINFO *pwsc);
|
||||||
|
@ -62,7 +64,7 @@ void xml_write(XMLSTRUCT *pxml, char *fmt, ...) {
|
||||||
if(pxml->psb) {
|
if(pxml->psb) {
|
||||||
xml_stream_write(pxml, buffer);
|
xml_stream_write(pxml, buffer);
|
||||||
} else {
|
} 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));
|
psb = (XML_STREAMBUFFER*) malloc(sizeof(XML_STREAMBUFFER));
|
||||||
if(!psb) {
|
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->out_buffer = (unsigned char*) malloc(XML_STREAM_BLOCK);
|
||||||
psb->in_buffer = (unsigned char*) malloc(XML_STREAM_BLOCK);
|
psb->in_buffer = (unsigned char*) malloc(XML_STREAM_BLOCK);
|
||||||
|
|
||||||
if((!psb->out_buffer) || (!psb->in_buffer)) {
|
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;
|
psb->strm.zalloc = Z_NULL;
|
||||||
|
@ -134,9 +136,9 @@ int xml_stream_write(XMLSTRUCT *pxml, char *out) {
|
||||||
while(!done) {
|
while(!done) {
|
||||||
result = deflate(&psb->strm, Z_NO_FLUSH);
|
result = deflate(&psb->strm, Z_NO_FLUSH);
|
||||||
if(result != Z_OK) {
|
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);
|
XML_STREAM_BLOCK-psb->strm.avail_out);
|
||||||
if(psb->strm.avail_out != 0) {
|
if(psb->strm.avail_out != 0) {
|
||||||
done=1;
|
done=1;
|
||||||
|
@ -163,14 +165,14 @@ int xml_stream_close(XMLSTRUCT *pxml) {
|
||||||
psb->strm.next_in = psb->in_buffer;
|
psb->strm.next_in = psb->in_buffer;
|
||||||
|
|
||||||
deflate(&psb->strm,Z_FINISH);
|
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);
|
XML_STREAM_BLOCK - psb->strm.avail_out);
|
||||||
|
|
||||||
if(psb->strm.avail_out != 0)
|
if(psb->strm.avail_out != 0)
|
||||||
done=1;
|
done=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
infn->log(E_DBG,"Done sending xml stream\n");
|
_ppi->log(E_DBG,"Done sending xml stream\n");
|
||||||
deflateEnd(&psb->strm);
|
deflateEnd(&psb->strm);
|
||||||
if(psb->out_buffer != NULL)
|
if(psb->out_buffer != NULL)
|
||||||
free(psb->out_buffer);
|
free(psb->out_buffer);
|
||||||
|
@ -196,7 +198,7 @@ XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
||||||
|
|
||||||
pxml=(XMLSTRUCT*)malloc(sizeof(XMLSTRUCT));
|
pxml=(XMLSTRUCT*)malloc(sizeof(XMLSTRUCT));
|
||||||
if(!pxml) {
|
if(!pxml) {
|
||||||
infn->log(E_FATAL,"Malloc error\n");
|
_ppi->log(E_FATAL,"Malloc error\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(pxml,0,sizeof(XMLSTRUCT));
|
memset(pxml,0,sizeof(XMLSTRUCT));
|
||||||
|
@ -204,27 +206,27 @@ XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
||||||
pxml->pwsc = pwsc;
|
pxml->pwsc = pwsc;
|
||||||
|
|
||||||
/* should we compress output? */
|
/* should we compress output? */
|
||||||
nogzip = infn->ws_getvar(pwsc,"nogzip");
|
nogzip = _ppi->ws_getvar(pwsc,"nogzip");
|
||||||
accept = infn->ws_getrequestheader(pwsc,"accept-encoding");
|
accept = _ppi->ws_getrequestheader(pwsc,"accept-encoding");
|
||||||
|
|
||||||
if((!nogzip) && (accept) && (strcasestr(accept,"gzip"))) {
|
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();
|
pxml->psb = xml_stream_open();
|
||||||
if(pxml->psb) {
|
if(pxml->psb) {
|
||||||
infn->ws_addresponseheader(pwsc,"Content-Encoding","gzip");
|
_ppi->ws_addresponseheader(pwsc,"Content-Encoding","gzip");
|
||||||
infn->ws_addresponseheader(pwsc,"Vary","Accept-Encoding");
|
_ppi->ws_addresponseheader(pwsc,"Vary","Accept-Encoding");
|
||||||
infn->ws_addresponseheader(pwsc,"Connection","Close");
|
_ppi->ws_addresponseheader(pwsc,"Connection","Close");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the world would be a wonderful place without ie */
|
/* the world would be a wonderful place without ie */
|
||||||
infn->ws_addresponseheader(pwsc,"Cache-Control","no-cache");
|
_ppi->ws_addresponseheader(pwsc,"Cache-Control","no-cache");
|
||||||
infn->ws_addresponseheader(pwsc,"Expires","-1");
|
_ppi->ws_addresponseheader(pwsc,"Expires","-1");
|
||||||
|
|
||||||
if(emit_header) {
|
if(emit_header) {
|
||||||
infn->ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
|
_ppi->ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
|
||||||
infn->ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
|
_ppi->ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
|
||||||
infn->ws_emitheaders(pwsc);
|
_ppi->ws_emitheaders(pwsc);
|
||||||
|
|
||||||
|
|
||||||
xml_write(pxml,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
|
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;
|
pstack=pxml->stack.next;
|
||||||
if(!pstack) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +312,7 @@ void xml_deinit(XMLSTRUCT *pxml) {
|
||||||
XMLSTACK *pstack;
|
XMLSTACK *pstack;
|
||||||
|
|
||||||
if(pxml->stack.next) {
|
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);
|
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