mirror of
https://github.com/owntone/owntone-server.git
synced 2025-11-10 22:10:15 -05:00
add ffmpeg-based transcoding
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#
|
||||
rspdir = ${pkgdatadir}/plugins
|
||||
ssc_ffmpegdir = ${pkgdatadir}/plugins
|
||||
ssc_scriptdir = ${pkgdatadir}/plugins
|
||||
|
||||
rsp_LTLIBRARIES=rsp.la
|
||||
rsp_la_LDFLAGS=-module -avoid-version
|
||||
@@ -13,7 +14,11 @@ ssc_ffmpeg_la_LDFLAGS=-module -avoid-version
|
||||
ssc_ffmpeg_la_SOURCES=ssc-ffmpeg.c
|
||||
endif
|
||||
|
||||
EXTRA_DIST = compat.h rsp.h mtd-plugins.h xml-rpc.h ssc-ffmpeg.c
|
||||
ssc_script_LTLIBRARIES=ssc-script.la
|
||||
ssc_script_la_LDFLAGS=-module -avoid-version
|
||||
ssc_script_la_SOURCES=ssc-script.c
|
||||
|
||||
EXTRA_DIST = compat.h rsp.h mtd-plugins.h xml-rpc.h ssc-ffmpeg.c ssc-script.c
|
||||
|
||||
AM_CFLAGS = -I..
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ typedef struct tag_rsp_privinfo {
|
||||
} PRIVINFO;
|
||||
|
||||
/* Forwards */
|
||||
PLUGIN_INFO *plugin_info(void);
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *);
|
||||
void plugin_handler(WS_CONNINFO *pwsc);
|
||||
int plugin_auth(WS_CONNINFO *pwsc, char *username, char *pw);
|
||||
void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi);
|
||||
@@ -39,6 +39,8 @@ PLUGIN_REND_INFO _pri[] = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
PLUGIN_INPUT_FN *_ppi;
|
||||
|
||||
PLUGIN_INFO _pi = {
|
||||
PLUGIN_VERSION, /* version */
|
||||
PLUGIN_OUTPUT, /* type */
|
||||
@@ -47,7 +49,6 @@ PLUGIN_INFO _pi = {
|
||||
&_pofn, /* output fns */
|
||||
NULL, /* event fns */
|
||||
NULL, /* transcode fns */
|
||||
NULL, /* ff functions */
|
||||
_pri, /* rend info */
|
||||
NULL /* transcode info */
|
||||
};
|
||||
@@ -145,7 +146,8 @@ FIELDSPEC rsp_fields[] = {
|
||||
/**
|
||||
* return info about this plugin module
|
||||
*/
|
||||
PLUGIN_INFO *plugin_info(void) {
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *ppi) {
|
||||
_ppi = ppi;
|
||||
return &_pi;
|
||||
}
|
||||
|
||||
@@ -168,7 +170,7 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
||||
int index, part;
|
||||
int found;
|
||||
|
||||
string = infn->ws_uri(pwsc);
|
||||
string = _ppi->ws_uri(pwsc);
|
||||
string++;
|
||||
|
||||
ppi = (PRIVINFO *)malloc(sizeof(PRIVINFO));
|
||||
@@ -177,27 +179,27 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
||||
}
|
||||
|
||||
if(!ppi) {
|
||||
infn->ws_returnerror(pwsc,500,"Malloc error in plugin_handler");
|
||||
_ppi->ws_returnerror(pwsc,500,"Malloc error in plugin_handler");
|
||||
return;
|
||||
}
|
||||
|
||||
memset((void*)&ppi->dq,0,sizeof(DB_QUERY));
|
||||
|
||||
infn->log(E_DBG,"Tokenizing url\n");
|
||||
_ppi->log(E_DBG,"Tokenizing url\n");
|
||||
while((ppi->uri_count < 10) && (token=strtok_r(string,"/",&save))) {
|
||||
string=NULL;
|
||||
ppi->uri_sections[ppi->uri_count++] = token;
|
||||
}
|
||||
|
||||
elements = sizeof(rsp_uri_map) / sizeof(PLUGIN_RESPONSE);
|
||||
infn->log(E_DBG,"Found %d elements\n",elements);
|
||||
_ppi->log(E_DBG,"Found %d elements\n",elements);
|
||||
|
||||
index = 0;
|
||||
found = 0;
|
||||
|
||||
while((!found) && (index < elements)) {
|
||||
/* test this set */
|
||||
infn->log(E_DBG,"Checking reponse %d\n",index);
|
||||
_ppi->log(E_DBG,"Checking reponse %d\n",index);
|
||||
part=0;
|
||||
while(part < 10) {
|
||||
if((rsp_uri_map[index].uri[part]) && (!ppi->uri_sections[part]))
|
||||
@@ -216,7 +218,7 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
||||
|
||||
if(part == 10) {
|
||||
found = 1;
|
||||
infn->log(E_DBG,"Found it! Index: %d\n",index);
|
||||
_ppi->log(E_DBG,"Found it! Index: %d\n",index);
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
@@ -224,13 +226,13 @@ void plugin_handler(WS_CONNINFO *pwsc) {
|
||||
|
||||
if(found) {
|
||||
rsp_uri_map[index].dispatch(pwsc, ppi);
|
||||
infn->ws_close(pwsc);
|
||||
_ppi->ws_close(pwsc);
|
||||
free(ppi);
|
||||
return;
|
||||
}
|
||||
|
||||
rsp_error(pwsc, ppi, 1, "Bad path");
|
||||
infn->ws_close(pwsc);
|
||||
_ppi->ws_close(pwsc);
|
||||
free(ppi);
|
||||
return;
|
||||
}
|
||||
@@ -243,7 +245,7 @@ void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
char servername[256];
|
||||
int size;
|
||||
|
||||
infn->log(E_DBG,"Starting rsp_info\n");
|
||||
_ppi->log(E_DBG,"Starting rsp_info\n");
|
||||
|
||||
pxml = xml_init(pwsc,1);
|
||||
|
||||
@@ -257,13 +259,13 @@ void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
|
||||
/* info block */
|
||||
xml_push(pxml,"info");
|
||||
xml_output(pxml,"count","%d",infn->db_count());
|
||||
xml_output(pxml,"count","%d",_ppi->db_count());
|
||||
xml_output(pxml,"rsp-version",RSP_VERSION);
|
||||
|
||||
xml_output(pxml,"server-version",infn->server_ver());
|
||||
xml_output(pxml,"server-version",_ppi->server_ver());
|
||||
|
||||
size = sizeof(servername);
|
||||
infn->server_name(servername,&size);
|
||||
_ppi->server_name(servername,&size);
|
||||
xml_output(pxml,"name",servername);
|
||||
xml_pop(pxml); /* info */
|
||||
|
||||
@@ -285,9 +287,9 @@ void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
|
||||
ppi->dq.query_type = QUERY_TYPE_PLAYLISTS;
|
||||
|
||||
if((err=infn->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
if((err=_ppi->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
rsp_error(pwsc, ppi, err | E_DB, pe);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -303,7 +305,7 @@ void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
|
||||
xml_push(pxml,"playlists");
|
||||
|
||||
while((infn->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
while((_ppi->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
xml_push(pxml,"playlist");
|
||||
rowindex=0;
|
||||
while(rsp_playlist_fields[rowindex].name) {
|
||||
@@ -316,8 +318,8 @@ void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
xml_pop(pxml); /* playlist */
|
||||
}
|
||||
|
||||
infn->db_enum_end(NULL);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_end(NULL);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
|
||||
xml_pop(pxml); /* playlists */
|
||||
xml_pop(pxml); /* response */
|
||||
@@ -343,7 +345,7 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
// char *user_agent;
|
||||
|
||||
/*
|
||||
user_agent = infn->ws_getrequestheader(pwsc,"user-agent");
|
||||
user_agent = _ppi->ws_getrequestheader(pwsc,"user-agent");
|
||||
if(user_agent) {
|
||||
if(strncmp(user_agent,"iTunes",6)==0) {
|
||||
trancode_codecs = "wma,ogg,flac,mpc";
|
||||
@@ -355,17 +357,17 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
}
|
||||
*/
|
||||
|
||||
ppi->dq.filter = infn->ws_getvar(pwsc,"query");
|
||||
ppi->dq.filter = _ppi->ws_getvar(pwsc,"query");
|
||||
ppi->dq.filter_type = FILTER_TYPE_FIREFLY;
|
||||
|
||||
if(infn->ws_getvar(pwsc,"offset")) {
|
||||
ppi->dq.offset = atoi(infn->ws_getvar(pwsc,"offset"));
|
||||
if(_ppi->ws_getvar(pwsc,"offset")) {
|
||||
ppi->dq.offset = atoi(_ppi->ws_getvar(pwsc,"offset"));
|
||||
}
|
||||
if(infn->ws_getvar(pwsc,"limit")) {
|
||||
ppi->dq.limit = atoi(infn->ws_getvar(pwsc,"limit"));
|
||||
if(_ppi->ws_getvar(pwsc,"limit")) {
|
||||
ppi->dq.limit = atoi(_ppi->ws_getvar(pwsc,"limit"));
|
||||
}
|
||||
|
||||
browse_type = infn->ws_getvar(pwsc,"type");
|
||||
browse_type = _ppi->ws_getvar(pwsc,"type");
|
||||
type = F_FULL;
|
||||
|
||||
if(browse_type) {
|
||||
@@ -378,9 +380,9 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
ppi->dq.query_type = QUERY_TYPE_ITEMS;
|
||||
ppi->dq.playlist_id = atoi(ppi->uri_sections[2]);
|
||||
|
||||
if((err=infn->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
if((err=_ppi->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
rsp_error(pwsc, ppi, err | E_DB, pe);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
free(pe);
|
||||
return;
|
||||
}
|
||||
@@ -395,7 +397,7 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
returned = ppi->dq.totalcount - ppi->dq.offset;
|
||||
}
|
||||
|
||||
transcode_codecs = infn->conf_alloc_string("general","ssc_codectypes","");
|
||||
transcode_codecs = _ppi->conf_alloc_string("general","ssc_codectypes","");
|
||||
|
||||
xml_push(pxml,"response");
|
||||
xml_push(pxml,"status");
|
||||
@@ -407,7 +409,7 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
|
||||
xml_push(pxml,"items");
|
||||
|
||||
while((infn->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
while((_ppi->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
xml_push(pxml,"item");
|
||||
rowindex=0;
|
||||
transcode = 0;
|
||||
@@ -453,8 +455,8 @@ void rsp_playlist(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
xml_pop(pxml); /* item */
|
||||
}
|
||||
|
||||
infn->db_enum_end(NULL);
|
||||
infn->conf_dispose_string(transcode_codecs);
|
||||
_ppi->db_enum_end(NULL);
|
||||
_ppi->conf_dispose_string(transcode_codecs);
|
||||
|
||||
xml_pop(pxml); /* items */
|
||||
xml_pop(pxml); /* response */
|
||||
@@ -471,22 +473,22 @@ void rsp_browse(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
/* this might fail if an unsupported browse type */
|
||||
ppi->dq.query_type = QUERY_TYPE_DISTINCT;
|
||||
ppi->dq.distinct_field = ppi->uri_sections[3];
|
||||
ppi->dq.filter = infn->ws_getvar(pwsc,"query");
|
||||
ppi->dq.filter = _ppi->ws_getvar(pwsc,"query");
|
||||
ppi->dq.filter_type = FILTER_TYPE_FIREFLY;
|
||||
|
||||
if(infn->ws_getvar(pwsc,"offset")) {
|
||||
ppi->dq.offset = atoi(infn->ws_getvar(pwsc,"offset"));
|
||||
if(_ppi->ws_getvar(pwsc,"offset")) {
|
||||
ppi->dq.offset = atoi(_ppi->ws_getvar(pwsc,"offset"));
|
||||
}
|
||||
|
||||
if(infn->ws_getvar(pwsc,"limit")) {
|
||||
ppi->dq.limit = atoi(infn->ws_getvar(pwsc,"limit"));
|
||||
if(_ppi->ws_getvar(pwsc,"limit")) {
|
||||
ppi->dq.limit = atoi(_ppi->ws_getvar(pwsc,"limit"));
|
||||
}
|
||||
|
||||
ppi->dq.playlist_id = atoi(ppi->uri_sections[2]);
|
||||
|
||||
if((err=infn->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
if((err=_ppi->db_enum_start(&pe,&ppi->dq)) != 0) {
|
||||
rsp_error(pwsc, ppi, err | E_DB, pe);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -510,12 +512,12 @@ void rsp_browse(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
|
||||
xml_push(pxml,"items");
|
||||
|
||||
while((infn->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
while((_ppi->db_enum_fetch_row(NULL,&row,&ppi->dq) == 0) && (row)) {
|
||||
xml_output(pxml,"item",row[0]);
|
||||
}
|
||||
|
||||
infn->db_enum_end(NULL);
|
||||
infn->db_enum_dispose(NULL,&ppi->dq);
|
||||
_ppi->db_enum_end(NULL);
|
||||
_ppi->db_enum_dispose(NULL,&ppi->dq);
|
||||
|
||||
xml_pop(pxml); /* items */
|
||||
xml_pop(pxml); /* response */
|
||||
@@ -523,7 +525,7 @@ void rsp_browse(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
}
|
||||
|
||||
void rsp_stream(WS_CONNINFO *pwsc, PRIVINFO *ppi) {
|
||||
infn->stream(pwsc, ppi->uri_sections[2]);
|
||||
_ppi->stream(pwsc, ppi->uri_sections[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -540,6 +542,6 @@ void rsp_error(WS_CONNINFO *pwsc, PRIVINFO *ppi, int eno, char *estr) {
|
||||
xml_pop(pxml); /* status */
|
||||
xml_pop(pxml); /* response */
|
||||
xml_deinit(pxml);
|
||||
infn->ws_close(pwsc);
|
||||
_ppi->ws_close(pwsc);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#define RSP_VERSION "1.0"
|
||||
|
||||
extern PLUGIN_INFO _pi;
|
||||
#define infn ((PLUGIN_INPUT_FN *)(_pi.pi))
|
||||
|
||||
#ifndef TRUE
|
||||
# define TRUE 1
|
||||
|
||||
@@ -40,6 +40,8 @@ typedef struct tag_ssc_handle {
|
||||
int buf_remainder_len;
|
||||
int first_frame;
|
||||
|
||||
int duration;
|
||||
|
||||
int total_decoded;
|
||||
int total_written;
|
||||
|
||||
@@ -64,19 +66,27 @@ typedef struct tag_ssc_handle {
|
||||
#define SSC_FFMPEG_E_FILEOPEN 3
|
||||
#define SSC_FFMPEG_E_NOSTREAM 4
|
||||
#define SSC_FFMPEG_E_NOAUDIO 5
|
||||
#define SSC_FFMPEG_E_BADFORMAT 6
|
||||
|
||||
char *ssc_ffmpeg_errors[] = {
|
||||
"Success",
|
||||
"Don't have appropriate codec",
|
||||
"Can't open codec",
|
||||
"Cannot open file",
|
||||
"Cannot find any streams",
|
||||
"No audio streams"
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Forwards */
|
||||
void *ssc_ffmpeg_init(void);
|
||||
void ssc_ffmpeg_deinit(void *pv);
|
||||
int ssc_ffmpeg_open(void *pv, char *file, char *codec);
|
||||
int ssc_ffmpeg_open(void *pv, char *file, char *codec, int duration);
|
||||
int ssc_ffmpeg_close(void *pv);
|
||||
int ssc_ffmpeg_read(void *pv, char *buffer, int len);
|
||||
char *ssc_ffmpeg_error(void *pv);
|
||||
|
||||
PLUGIN_INFO *plugin_info(void);
|
||||
|
||||
#define infn ((PLUGIN_INPUT_FN *)(_pi.pi))
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN*);
|
||||
|
||||
/* Globals */
|
||||
PLUGIN_TRANSCODE_FN _ptfn = {
|
||||
@@ -84,9 +94,12 @@ PLUGIN_TRANSCODE_FN _ptfn = {
|
||||
ssc_ffmpeg_deinit,
|
||||
ssc_ffmpeg_open,
|
||||
ssc_ffmpeg_close,
|
||||
ssc_ffmpeg_read
|
||||
ssc_ffmpeg_read,
|
||||
ssc_ffmpeg_error
|
||||
};
|
||||
|
||||
PLUGIN_INPUT_FN *_ppi;
|
||||
|
||||
PLUGIN_INFO _pi = {
|
||||
PLUGIN_VERSION, /* version */
|
||||
PLUGIN_TRANSCODE, /* type */
|
||||
@@ -95,12 +108,18 @@ PLUGIN_INFO _pi = {
|
||||
NULL, /* output fns */
|
||||
NULL, /* event fns */
|
||||
&_ptfn, /* fns */
|
||||
NULL, /* functions exported by ff */
|
||||
NULL, /* rend info */
|
||||
"flac,alac,ogg,wma" /* codeclist */
|
||||
};
|
||||
|
||||
PLUGIN_INFO *plugin_info(void) {
|
||||
char *ssc_ffmpeg_error(void *pv) {
|
||||
SSCHANDLE *handle = (SSCHANDLE*)pv;
|
||||
|
||||
return ssc_ffmpeg_errors[handle->errno];
|
||||
}
|
||||
|
||||
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *ppi) {
|
||||
_ppi = ppi;
|
||||
av_register_all();
|
||||
|
||||
return &_pi;
|
||||
@@ -127,7 +146,7 @@ void ssc_ffmpeg_deinit(void *vp) {
|
||||
return;
|
||||
}
|
||||
|
||||
int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||
int ssc_ffmpeg_open(void *vp, char *file, char *codec, int duration) {
|
||||
int i;
|
||||
enum CodecID id=CODEC_ID_FLAC;
|
||||
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
||||
@@ -135,10 +154,11 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||
if(!handle)
|
||||
return FALSE;
|
||||
|
||||
handle->duration = duration;
|
||||
handle->first_frame = 1;
|
||||
handle->raw=0;
|
||||
|
||||
infn->log(E_DBG,"opening %s\n",file);
|
||||
_ppi->log(E_DBG,"opening %s\n",file);
|
||||
|
||||
if(strcasecmp(codec,"flac") == 0) {
|
||||
handle->raw=1;
|
||||
@@ -146,6 +166,7 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||
}
|
||||
|
||||
if(handle->raw) {
|
||||
_ppi->log(E_DBG,"opening file raw\n");
|
||||
handle->pCodec = avcodec_find_decoder(id);
|
||||
if(!handle->pCodec) {
|
||||
handle->errno = SSC_FFMPEG_E_BADCODEC;
|
||||
@@ -166,7 +187,8 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
_ppi->log(E_DBG,"opening file with format\n");
|
||||
if(av_open_input_file(&handle->pFmtCtx,file,handle->pFormat,0,NULL) < 0) {
|
||||
handle->errno = SSC_FFMPEG_E_FILEOPEN;
|
||||
return FALSE;
|
||||
@@ -178,7 +200,7 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dump_format(handle->pFmtCtx,0,file,FALSE);
|
||||
// dump_format(handle->pFmtCtx,0,file,FALSE);
|
||||
|
||||
handle->audio_stream = -1;
|
||||
for(i=0; i < handle->pFmtCtx->nb_streams; i++) {
|
||||
@@ -226,8 +248,10 @@ int ssc_ffmpeg_close(void *vp) {
|
||||
if(handle->pFrame)
|
||||
av_free(handle->pFrame);
|
||||
|
||||
if(handle->pCodecCtx)
|
||||
avcodec_close(handle->pCodecCtx);
|
||||
if(handle->raw) {
|
||||
if(handle->pCodecCtx)
|
||||
avcodec_close(handle->pCodecCtx);
|
||||
}
|
||||
|
||||
if(handle->pFmtCtx)
|
||||
av_close_input_file(handle->pFmtCtx);
|
||||
@@ -393,14 +417,17 @@ int ssc_ffmpeg_read(void *vp, char *buffer, int len) {
|
||||
handle->swab = (bits_per_sample == 16) &&
|
||||
(memcmp((void*)&test1,test2,2) == 0);
|
||||
|
||||
data_len = (bits_per_sample * sample_rate * channels * (duration/1000));
|
||||
if(handle->duration)
|
||||
duration = handle->duration;
|
||||
|
||||
data_len = ((bits_per_sample * sample_rate * channels / 8) * (duration/1000));
|
||||
byte_rate = sample_rate * channels * bits_per_sample / 8;
|
||||
block_align = channels * bits_per_sample / 8;
|
||||
|
||||
infn->log(E_DBG,"Channels.......: %d\n",channels);
|
||||
infn->log(E_DBG,"Sample rate....: %d\n",sample_rate);
|
||||
infn->log(E_DBG,"Bits/Sample....: %d\n",bits_per_sample);
|
||||
infn->log(E_DBG,"Swab...........: %d\n",handle->swab);
|
||||
_ppi->log(E_DBG,"Channels.......: %d\n",channels);
|
||||
_ppi->log(E_DBG,"Sample rate....: %d\n",sample_rate);
|
||||
_ppi->log(E_DBG,"Bits/Sample....: %d\n",bits_per_sample);
|
||||
_ppi->log(E_DBG,"Swab...........: %d\n",handle->swab);
|
||||
|
||||
memcpy(&handle->wav_header[0],"RIFF",4);
|
||||
_ssc_ffmpeg_le32(&handle->wav_header[4],36 + data_len);
|
||||
|
||||
206
src/plugins/ssc-script.c
Normal file
206
src/plugins/ssc-script.c
Normal 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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user