mirror of
https://gitlab.xiph.org/xiph/ezstream.git
synced 2024-11-03 04:17:18 -05:00
Added reencoding capabilities to ezstream.
git-svn-id: https://svn.xiph.org/trunk/ezstream@7170 0101bb08-14d6-0310-b084-bc0e0c8e3800
This commit is contained in:
parent
ef2bdf0ba2
commit
0968f1773c
85
README
85
README
@ -10,7 +10,7 @@ server without reencoding and thus requires very little CPU. ezstream is
|
||||
controlled via a XML config file (a few examples are provided in the conf
|
||||
directory).
|
||||
|
||||
ezstream can stream mp3 and ogg vorbis files as well as reading from stdin.
|
||||
ezstream can stream mp3, ogg vorbis, and ogg theora files as well as reading from stdin.
|
||||
ID3v1 tags are supported in mp3 files and all ogg vorbis tags are propagated
|
||||
as metadata as well.
|
||||
|
||||
@ -23,7 +23,7 @@ The following is an example config file :
|
||||
<ezstream>
|
||||
<url>http://localhost:8000/testmount.ogg</url>
|
||||
<sourcepassword>hackme</sourcepassword>
|
||||
<format>OGGVORBIS</format>
|
||||
<format>VORBIS</format>
|
||||
<filename>sunking.ogg</filename>
|
||||
<svrinfoname>My Stream</svrinfoname>
|
||||
<svrinfourl>http://www.oddsock.org</svrinfourl>
|
||||
@ -34,13 +34,17 @@ The following is an example config file :
|
||||
<svrinfochannels>2</svrinfochannels>
|
||||
<svrinfosamplerate>44100</svrinfosamplerate>
|
||||
<svrinfopublic>1</svrinfopublic>
|
||||
<reencode>
|
||||
<enable>0</enable>
|
||||
</reencode>
|
||||
</ezstream>
|
||||
|
||||
URL - this URL specified the location and mountpoint of the icecast server
|
||||
to which the stream will be sent.
|
||||
SOURCEPASSWORD - the source password for the icecast server
|
||||
FORMAT - either MP3 or OGGVORBIS, you must specify which format you input
|
||||
files are in.
|
||||
FORMAT - either MP3, VORBIS or THEORA, This is the output format of your stream.
|
||||
If you are not reencoding, then this also must be the same format as your
|
||||
input files.
|
||||
FILENAME - This can be a single file (in which ezstream will stream that
|
||||
file over and over continuously) or can be a .m3u file which
|
||||
is a playlist of files. currently, ezstream will go through
|
||||
@ -60,3 +64,76 @@ SVRINFOCHANNELS - 1 = MONO, 2 = STEREO (informational only) (used for YP)
|
||||
SVRINFOSAMPLERATE - (informational only) (used for YP)
|
||||
SVRINFOPUBLIC - Indicates wether to list this stream in a public YP.
|
||||
|
||||
|
||||
REENCODING
|
||||
:::::::::::::::
|
||||
ezstream now support reencoding. This means that your output stream need not
|
||||
be the same bitrate/samplerate or even format as your input files.
|
||||
|
||||
Reencoding is supported via the use of external programs. When you enable reencoding
|
||||
you need to make sure of a few things :
|
||||
|
||||
1. You define a "decoder" for each type of input file.
|
||||
2. You define a "encoder" for each possible type of output stream.
|
||||
|
||||
So if you had a mixture of mp3 and vorbis files in your playlist, you will need to make
|
||||
sure that a decoder is provided for each type. Ezstream will take the output of the
|
||||
decoder and send it directly to the specific encoder. All output of the decoder should
|
||||
be written to stdout and should be in raw form. Most decoder support this mode. Encoders
|
||||
should all be configured also with raw input and should read it from stdin.
|
||||
|
||||
The following decoder/encoders can be used :
|
||||
|
||||
For MP3 :
|
||||
decoder : madplay
|
||||
encoder : lame
|
||||
|
||||
For Vorbis :
|
||||
decoder : oggdec
|
||||
encoder : oggenc
|
||||
|
||||
For FLAC :
|
||||
decoder : FLAC
|
||||
encoder : not yet supported by libshout, and thus not by ezstream.
|
||||
|
||||
Additional decoders and encoders may be used, as long as they follow the rules defined above.
|
||||
|
||||
The following config file section defines the reencoding parameters :
|
||||
|
||||
<reencode>
|
||||
<enable>1</enable>
|
||||
<encdec>
|
||||
<!-- Support for FLAC decoding (input files) -->
|
||||
<format>FLAC</format> <!-- format = output stream format -->
|
||||
<match>.flac</match> <!-- match = input file format -->
|
||||
<decode>flac -s -d --force-raw-format --sign=signed --endian=little @T@ -o -</decode>
|
||||
<encode>Not supported Yet</encode>
|
||||
</encdec>
|
||||
<encdec>
|
||||
<!-- Support for MP3 decoding via madplay, and encoding via LAME -->
|
||||
<format>MP3</format>
|
||||
<match>.mp3</match>
|
||||
<decode>madplay -o raw:- @T@ 2>/dev/null</decode>
|
||||
<encode>lame -r -x -b 56 -s 44.1 --resample 22.05 -a - - 2>/dev/null</encode>
|
||||
</encdec>
|
||||
<encdec>
|
||||
<!-- Support for Vorbis decoding via oggdec, and encoding via oggenc -->
|
||||
<format>VORBIS</format>
|
||||
<match>.ogg</match>
|
||||
<decode>oggdec --raw=1 @T@ -o - 2>/dev/null</decode>
|
||||
<encode>oggenc -Q -r -q 0 --resample=44100 --downmix -t "@M@" -c STREAMER=ezstream -</encode>
|
||||
</encdec>
|
||||
</reencode>
|
||||
|
||||
|
||||
Note that the following keywords can be used :
|
||||
|
||||
@T@ = The fully qualified name of the track being played in the playlist
|
||||
@M@ = The metadata for the current track
|
||||
|
||||
All encoding options (bitrate/samplerate/channels/quality) are set as command line options of
|
||||
each of the encoders. Each encoder has slightly different options that control these values.
|
||||
The examples here can be used as a guide, but please make sure you check the manual for each
|
||||
encoder for the correct encoding options. In all cases, the encoder should be configured to
|
||||
read RAW audio data from stdin.
|
||||
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
EXTRA_DIST = ezstream_m3u.xml ezstream_mp3.xml ezstream_vorbis.xml
|
||||
EXTRA_DIST = ezstream_m3u.xml ezstream_mp3.xml ezstream_vorbis.xml ezstream_reencoding_example.xml
|
||||
|
60
conf/ezstream_reencoding_example.xml
Normal file
60
conf/ezstream_reencoding_example.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<ezstream>
|
||||
<url>http://192.168.6.1:8000/testmount.ogg</url>
|
||||
<sourcepassword>hackme</sourcepassword>
|
||||
<!-- This is what form your output will take. If you are
|
||||
reencoding, this is the format to reencode to, if not
|
||||
then you need to make sure all your input files are in this
|
||||
format -->
|
||||
<format>VORBIS</format>
|
||||
<filename>tracks.m3u</filename>
|
||||
<!-- The following settings are used to describe your stream
|
||||
to the server. It's up to you to make sure the
|
||||
bitrate/quality/samplerate/channels
|
||||
match up to your output stream -->
|
||||
<svrinfoname>My Stream</svrinfoname>
|
||||
<svrinfourl>http://www.oddsock.org</svrinfourl>
|
||||
<svrinfogenre>RockNRoll</svrinfogenre>
|
||||
<svrinfodescription>This is a stream description</svrinfodescription>
|
||||
<svrinfobitrate>128</svrinfobitrate>
|
||||
<!-- Quality is only applicable to ogg vorbis streams -->
|
||||
<!-- <svrinfoquality>1.0</svrinfoquality> -->
|
||||
<svrinfochannels>2</svrinfochannels>
|
||||
<svrinfosamplerate>44100</svrinfosamplerate>
|
||||
<svrinfopublic>1</svrinfopublic>
|
||||
<reencode>
|
||||
<enable>1</enable>
|
||||
<!-- Each encdec block specifies a pair of programs used for decoding and
|
||||
encoding of the stream. If reencoding is enabled, then all input files
|
||||
must be first decoded before being sent to the encoder. EZSTREAM uses
|
||||
file extensions to match up input files with the appropraite decoder,
|
||||
and uses the <format> setting to match up the output format with the
|
||||
appropriate encoder.
|
||||
|
||||
Note: It it up to you to set the appropriate bitrate/samplerate/channels
|
||||
of the output stream by using command line paramters to the encoders. Use
|
||||
the examples defined here as a guide. All output from decoders should be in
|
||||
RAW format, and all input to the encoders should also be in RAW format. -->
|
||||
<encdec>
|
||||
<!-- Support for FLAC decoding (input files) -->
|
||||
<format>FLAC</format>
|
||||
<match>.flac</match>
|
||||
<decode>flac -s -d --force-raw-format --sign=signed --endian=little @T@ -o -</decode>
|
||||
<encode>Not supported Yet</encode>
|
||||
</encdec>
|
||||
<encdec>
|
||||
<!-- Support for MP3 decoding via madplay, and encoding via LAME -->
|
||||
<format>MP3</format>
|
||||
<match>.mp3</match>
|
||||
<decode>madplay -o raw:- @T@ 2>/dev/null</decode>
|
||||
<encode>lame -r -x -b 56 -s 44.1 --resample 22.05 -a - - 2>/dev/null</encode>
|
||||
</encdec>
|
||||
<encdec>
|
||||
<!-- Support for Vorbis decoding via oggdec, and encoding via oggenc -->
|
||||
<format>VORBIS</format>
|
||||
<match>.ogg</match>
|
||||
<decode>oggdec --raw=1 @T@ -o - 2>/dev/null</decode>
|
||||
<encode>oggenc -Q -r -q 0 --resample=44100 --downmix -t "@M@" -c STREAMER=ezstream -</encode>
|
||||
</encdec>
|
||||
<!-- New encdec sections can be added for new input/output formats -->
|
||||
</reencode>
|
||||
</ezstream>
|
@ -77,8 +77,8 @@ XIPH_VAR_APPEND([XIPH_CFLAGS], [$SHOUT_CFLAGS])
|
||||
XIPH_VAR_PREPEND([XIPH_LIBS], [$SHOUT_LIBS])
|
||||
|
||||
XIPH_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 installed!]))
|
||||
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS])
|
||||
XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS])
|
||||
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS $VORBISFILE_CFLAGS])
|
||||
XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS $VORBISFILE_LIBS])
|
||||
|
||||
dnl Make substitutions
|
||||
|
||||
|
166
src/configfile.c
166
src/configfile.c
@ -2,13 +2,54 @@
|
||||
#include "configfile.h"
|
||||
|
||||
static EZCONFIG ezConfig;
|
||||
static char *blankString = "";
|
||||
|
||||
EZCONFIG *getEZConfig() {
|
||||
return &ezConfig;
|
||||
}
|
||||
|
||||
char* getFormatEncoder(char *format)
|
||||
{
|
||||
int i = 0;
|
||||
for (i=0;i<ezConfig.numEncoderDecoders;i++) {
|
||||
if (ezConfig.encoderDecoders[i]) {
|
||||
if (ezConfig.encoderDecoders[i]->format) {
|
||||
if (!strcmp(ezConfig.encoderDecoders[i]->format, format)) {
|
||||
if (ezConfig.encoderDecoders[i]->encoder) {
|
||||
return ezConfig.encoderDecoders[i]->encoder;
|
||||
}
|
||||
else {
|
||||
return blankString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return blankString;
|
||||
}
|
||||
|
||||
char* getFormatDecoder(char *match)
|
||||
{
|
||||
int i = 0;
|
||||
for (i=0;i<ezConfig.numEncoderDecoders;i++) {
|
||||
if (ezConfig.encoderDecoders[i]) {
|
||||
if (ezConfig.encoderDecoders[i]->match) {
|
||||
if (!strcmp(ezConfig.encoderDecoders[i]->match, match)) {
|
||||
if (ezConfig.encoderDecoders[i]->decoder) {
|
||||
return ezConfig.encoderDecoders[i]->decoder;
|
||||
}
|
||||
else {
|
||||
return blankString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return blankString;
|
||||
}
|
||||
void printConfig()
|
||||
{
|
||||
int i = 0;
|
||||
if (ezConfig.URL) {
|
||||
printf("URL to connect to (%s)\n", ezConfig.URL);
|
||||
}
|
||||
@ -24,8 +65,11 @@ void printConfig()
|
||||
if (ezConfig.format == MP3_FORMAT) {
|
||||
printf("Broadcasting in MP3 format\n");
|
||||
}
|
||||
if (ezConfig.format == OGG_FORMAT) {
|
||||
printf("Broadcasting in Ogg format\n");
|
||||
if (ezConfig.format == VORBIS_FORMAT) {
|
||||
printf("Broadcasting in Ogg Vorbis format\n");
|
||||
}
|
||||
if (ezConfig.format == THEORA_FORMAT) {
|
||||
printf("Broadcasting in Ogg Theora format\n");
|
||||
}
|
||||
if (ezConfig.format == 0) {
|
||||
printf("Broadcast format not set\n");
|
||||
@ -90,6 +134,44 @@ void printConfig()
|
||||
else {
|
||||
printf("Server is a private server\n");
|
||||
}
|
||||
if (ezConfig.reencode) {
|
||||
printf("We will reencode using the following information:\n");
|
||||
printf("\tEncoders/Decoders:\n");
|
||||
for (i=0;i<ezConfig.numEncoderDecoders;i++) {
|
||||
if (ezConfig.encoderDecoders[i]) {
|
||||
if (ezConfig.encoderDecoders[i]->match) {
|
||||
if (ezConfig.encoderDecoders[i]->decoder) {
|
||||
printf("\t\tFor files of extension (%s)\n", ezConfig.encoderDecoders[i]->match);
|
||||
printf("\t\t\tDecoder: (%s)\n", ezConfig.encoderDecoders[i]->decoder);
|
||||
}
|
||||
else {
|
||||
printf("\t\tNull decoder\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("\t\tNull match\n");
|
||||
}
|
||||
if (ezConfig.encoderDecoders[i]->format) {
|
||||
if (ezConfig.encoderDecoders[i]->encoder) {
|
||||
printf("\t\tFor output formats of type (%s)\n", ezConfig.encoderDecoders[i]->format);
|
||||
printf("\t\t\tEncoder: (%s)\n", ezConfig.encoderDecoders[i]->encoder);
|
||||
}
|
||||
else {
|
||||
printf("\t\tNull encoder\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("\t\tNull match\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Error, NULL GRABBER\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("We will NOT reencode.\n");
|
||||
}
|
||||
|
||||
}
|
||||
int parseConfig(char *fileName)
|
||||
@ -142,12 +224,9 @@ int parseConfig(char *fileName)
|
||||
if (cur->xmlChildrenNode != NULL) {
|
||||
ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode,1);
|
||||
if ( strlen(ls_xmlContentPtr) > 0 ) {
|
||||
if (!strcmp(ls_xmlContentPtr, "MP3")) {
|
||||
ezConfig.format = MP3_FORMAT;
|
||||
}
|
||||
if (!strcmp(ls_xmlContentPtr, "OGG")) {
|
||||
ezConfig.format = OGG_FORMAT;
|
||||
}
|
||||
ezConfig.format = (char *)malloc(strlen(ls_xmlContentPtr) +1);
|
||||
memset(ezConfig.format, '\000', strlen(ls_xmlContentPtr) +1);
|
||||
strcpy(ezConfig.format, ls_xmlContentPtr);
|
||||
}
|
||||
xmlFree(ls_xmlContentPtr);
|
||||
}
|
||||
@ -261,6 +340,77 @@ int parseConfig(char *fileName)
|
||||
xmlFree(ls_xmlContentPtr);
|
||||
}
|
||||
}
|
||||
if (!xmlStrcmp(cur->name, (const xmlChar *) "reencode")) {
|
||||
xmlNodePtr cur2;
|
||||
cur2 = cur->xmlChildrenNode;
|
||||
while (cur2 != NULL) {
|
||||
if (!xmlStrcmp(cur2->name, (const xmlChar *) "enable")) {
|
||||
if (cur2->xmlChildrenNode != NULL) {
|
||||
ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, cur2->xmlChildrenNode,1);
|
||||
if ( strlen(ls_xmlContentPtr) > 0 ) {
|
||||
ezConfig.reencode = atoi(ls_xmlContentPtr);
|
||||
}
|
||||
xmlFree(ls_xmlContentPtr);
|
||||
}
|
||||
}
|
||||
if (!xmlStrcmp(cur2->name, (const xmlChar *) "encdec")) {
|
||||
FORMAT_ENCDEC *pformatEncDec = malloc(sizeof(FORMAT_ENCDEC));
|
||||
memset(pformatEncDec, '\000', sizeof(FORMAT_ENCDEC));
|
||||
xmlNodePtr cur3;
|
||||
cur3 = cur2->xmlChildrenNode;
|
||||
while (cur3 != NULL) {
|
||||
if (!xmlStrcmp(cur3->name, (const xmlChar *) "format")) {
|
||||
if (cur3->xmlChildrenNode != NULL) {
|
||||
ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, cur3->xmlChildrenNode,1);
|
||||
if ( strlen(ls_xmlContentPtr) > 0 ) {
|
||||
pformatEncDec->format = (char *)malloc(strlen(ls_xmlContentPtr) +1);
|
||||
memset(pformatEncDec->format, '\000', strlen(ls_xmlContentPtr) +1);
|
||||
strcpy(pformatEncDec->format, ls_xmlContentPtr);
|
||||
}
|
||||
xmlFree(ls_xmlContentPtr);
|
||||
}
|
||||
}
|
||||
if (!xmlStrcmp(cur3->name, (const xmlChar *) "match")) {
|
||||
if (cur3->xmlChildrenNode != NULL) {
|
||||
ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, cur3->xmlChildrenNode,1);
|
||||
if ( strlen(ls_xmlContentPtr) > 0 ) {
|
||||
pformatEncDec->match = (char *)malloc(strlen(ls_xmlContentPtr) +1);
|
||||
memset(pformatEncDec->match, '\000', strlen(ls_xmlContentPtr) +1);
|
||||
strcpy(pformatEncDec->match, ls_xmlContentPtr);
|
||||
}
|
||||
xmlFree(ls_xmlContentPtr);
|
||||
}
|
||||
}
|
||||
if (!xmlStrcmp(cur3->name, (const xmlChar *) "decode")) {
|
||||
if (cur3->xmlChildrenNode != NULL) {
|
||||
ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, cur3->xmlChildrenNode,1);
|
||||
if ( strlen(ls_xmlContentPtr) > 0 ) {
|
||||
pformatEncDec->decoder = (char *)malloc(strlen(ls_xmlContentPtr) +1);
|
||||
memset(pformatEncDec->decoder, '\000', strlen(ls_xmlContentPtr) +1);
|
||||
strcpy(pformatEncDec->decoder, ls_xmlContentPtr);
|
||||
}
|
||||
xmlFree(ls_xmlContentPtr);
|
||||
}
|
||||
}
|
||||
if (!xmlStrcmp(cur3->name, (const xmlChar *) "encode")) {
|
||||
if (cur3->xmlChildrenNode != NULL) {
|
||||
ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, cur3->xmlChildrenNode,1);
|
||||
if ( strlen(ls_xmlContentPtr) > 0 ) {
|
||||
pformatEncDec->encoder = (char *)malloc(strlen(ls_xmlContentPtr) +1);
|
||||
memset(pformatEncDec->encoder, '\000', strlen(ls_xmlContentPtr) +1);
|
||||
strcpy(pformatEncDec->encoder, ls_xmlContentPtr);
|
||||
}
|
||||
xmlFree(ls_xmlContentPtr);
|
||||
}
|
||||
}
|
||||
cur3 = cur3->next;
|
||||
}
|
||||
ezConfig.encoderDecoders[ezConfig.numEncoderDecoders] = pformatEncDec;
|
||||
ezConfig.numEncoderDecoders++;
|
||||
}
|
||||
cur2 = cur2->next;
|
||||
}
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
return(1);
|
||||
|
@ -4,13 +4,23 @@
|
||||
#include <libxml/parser.h>
|
||||
|
||||
|
||||
#define MP3_FORMAT 1
|
||||
#define OGG_FORMAT 2
|
||||
#define MP3_FORMAT "MP3"
|
||||
#define VORBIS_FORMAT "VORBIS"
|
||||
#define THEORA_FORMAT "THEORA"
|
||||
|
||||
#define MAX_FORMAT_ENCDEC 15
|
||||
|
||||
typedef struct tag_FORMAT_ENCDEC {
|
||||
char *format;
|
||||
char *match;
|
||||
char *encoder;
|
||||
char *decoder;
|
||||
} FORMAT_ENCDEC;
|
||||
|
||||
typedef struct tag_EZCONFIG {
|
||||
char *URL;
|
||||
char *password;
|
||||
int format;
|
||||
char *format;
|
||||
char *fileName;
|
||||
char *serverName;
|
||||
char *serverURL;
|
||||
@ -21,9 +31,18 @@ typedef struct tag_EZCONFIG {
|
||||
char *serverSamplerate;
|
||||
char *serverQuality;
|
||||
int serverPublic;
|
||||
int reencode;
|
||||
FORMAT_ENCDEC *encoderDecoders[MAX_FORMAT_ENCDEC];
|
||||
int numEncoderDecoders;
|
||||
} EZCONFIG;
|
||||
|
||||
|
||||
|
||||
void printConfig();
|
||||
int parseConfig(char *fileName);
|
||||
EZCONFIG *getEZConfig();
|
||||
char* getFormatEncoder(char *format);
|
||||
char* getFormatDecoder(char *match);
|
||||
char* getMetadataGrabber(char *match);
|
||||
|
||||
#endif
|
||||
|
362
src/ezstream.c
362
src/ezstream.c
@ -10,9 +10,14 @@
|
||||
#include <shout/shout.h>
|
||||
#include <getopt.h>
|
||||
#include "configfile.h"
|
||||
#ifndef WIN32
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
#include <vorbis/vorbisfile.h>
|
||||
|
||||
EZCONFIG *pezConfig = NULL;
|
||||
int rereadPlaylist = 0;
|
||||
static char *blankString = "";
|
||||
|
||||
#ifndef WIN32
|
||||
#include <signal.h>
|
||||
@ -23,6 +28,12 @@ void hup_handler(int sig)
|
||||
printf("Will reread the playlist on next song\n");
|
||||
}
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
#define STRNCASECMP strnicmp
|
||||
#define popen _popen
|
||||
#else
|
||||
#define STRNCASECMP strncasecmp
|
||||
#endif
|
||||
|
||||
typedef struct tag_ID3Tag {
|
||||
char tag[3];
|
||||
@ -87,44 +98,241 @@ int urlParse(char *url, char *hostname, int *port, char *mountname)
|
||||
|
||||
}
|
||||
|
||||
void ReplaceString(char *source, char *dest, char *from, char *to)
|
||||
{
|
||||
char *p2 = (char *)1;
|
||||
char *p1 = source;
|
||||
while (p2) {
|
||||
p2 = strstr(p1, from);
|
||||
if (p2) {
|
||||
strncat(dest, p1, p2-p1);
|
||||
strcat(dest, to);
|
||||
p1 = p2 + strlen(from);
|
||||
}
|
||||
else {
|
||||
strcat(dest, p1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setMetadata(shout_t *shout, char *metadata)
|
||||
{
|
||||
shout_metadata_t *shoutMetadata = shout_metadata_new();
|
||||
shout_metadata_add(shoutMetadata, "song", metadata);
|
||||
shout_set_metadata(shout, shoutMetadata);
|
||||
shout_metadata_free(shoutMetadata);
|
||||
}
|
||||
|
||||
char* buildCommandString(char *extension, char *fileName, char *metadata)
|
||||
{
|
||||
char *commandString = NULL;
|
||||
char *encoder = NULL;
|
||||
char *decoder = NULL;
|
||||
int newDecoderLen = 0;
|
||||
char *newDecoder = NULL;
|
||||
char *newEncoder = NULL;
|
||||
int newEncoderLen = 0;
|
||||
int commandStringLen = 0;
|
||||
|
||||
decoder = strdup(getFormatDecoder(extension));
|
||||
if (strlen(decoder) == 0) {
|
||||
printf("Unknown extension %s, cannot decode\n", extension);
|
||||
return commandString;
|
||||
}
|
||||
encoder = strdup(getFormatEncoder(pezConfig->format));
|
||||
if (strlen(encoder) == 0) {
|
||||
printf("Unknown format %s, cannot encode\n", pezConfig->format);
|
||||
return commandString;
|
||||
}
|
||||
newDecoderLen = strlen(decoder) + strlen(fileName) + 1;
|
||||
newDecoder = (char *)malloc(newDecoderLen);
|
||||
memset(newDecoder, '\000', newDecoderLen);
|
||||
ReplaceString(decoder, newDecoder, "@T@", fileName);
|
||||
|
||||
newEncoderLen = strlen(encoder) + strlen(metadata) + 1;
|
||||
newEncoder = (char *)malloc(newEncoderLen);
|
||||
memset(newEncoder, '\000', newEncoderLen);
|
||||
ReplaceString(encoder, newEncoder, "@M@", metadata);
|
||||
|
||||
commandStringLen = strlen(newDecoder) + strlen(" | ") + strlen(newEncoder) + 1;
|
||||
commandString = (char *)malloc(commandStringLen);
|
||||
memset(commandString, '\000', commandStringLen);
|
||||
sprintf(commandString, "%s | %s", newDecoder, newEncoder);
|
||||
printf("Going to execute (%s)\n", commandString);
|
||||
return(commandString);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
char *basename(char *fileName) {
|
||||
char *pLast = strrchr(fileName, '\\');
|
||||
if (pLast) {
|
||||
return pLast+1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
char * processMetadata(shout_t *shout, char *extension, char *fileName) {
|
||||
FILE *filepstream = NULL;
|
||||
char *artist = NULL;
|
||||
char *title = NULL;
|
||||
char *songInfo = NULL;
|
||||
int songLen = 0;
|
||||
ID3Tag id3tag;
|
||||
|
||||
filepstream = fopen(fileName, "rb");
|
||||
if (filepstream == NULL) {
|
||||
printf("Cannot open (%s) - No metadata support.\n", fileName);
|
||||
return strdup(blankString);
|
||||
}
|
||||
|
||||
if (!strcmp(extension, ".mp3")) {
|
||||
/* Look for the ID3 tag */
|
||||
if (filepstream) {
|
||||
memset(&id3tag, '\000', sizeof(id3tag));
|
||||
fseek(filepstream, -128L, SEEK_END);
|
||||
fread(&id3tag, 1, 127, filepstream);
|
||||
if (!strncmp(id3tag.tag, "TAG", strlen("TAG"))) {
|
||||
/* We have an Id3 tag */
|
||||
songLen = strlen(id3tag.artistName) + strlen(" - ") + strlen(id3tag.trackName);
|
||||
songInfo = (char *)malloc(songLen);
|
||||
memset(songInfo, '\000', songLen);
|
||||
|
||||
sprintf(songInfo, "%s - %s", id3tag.artistName, id3tag.trackName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!strcmp(extension, ".ogg")) {
|
||||
OggVorbis_File vf;
|
||||
if(ov_open(filepstream, &vf, NULL, 0) < 0) {
|
||||
printf("Input does not appear to be an Ogg Vorbis bitstream. No metadata support.\n");
|
||||
}
|
||||
else {
|
||||
char **ptr=ov_comment(&vf,-1)->user_comments;
|
||||
while(*ptr){
|
||||
if (!STRNCASECMP(*ptr, "ARTIST", strlen("ARTIST"))) {
|
||||
artist = (char *)strdup(*ptr + strlen("ARTIST="));
|
||||
}
|
||||
if (!STRNCASECMP(*ptr, "TITLE", strlen("TITLE"))) {
|
||||
title = (char *)strdup(*ptr + strlen("TITLE="));
|
||||
}
|
||||
++ptr;
|
||||
}
|
||||
if (artist) {
|
||||
songLen = songLen + strlen(artist);
|
||||
}
|
||||
if (title) {
|
||||
songLen = songLen + strlen(title);
|
||||
}
|
||||
songLen = songLen + strlen(" - ") + 1;
|
||||
songInfo = (char *)malloc(songLen);
|
||||
memset(songInfo, '\000', songLen);
|
||||
if (artist) {
|
||||
strcat(songInfo, artist);
|
||||
strcat(songInfo, " - ");
|
||||
free(artist);
|
||||
}
|
||||
if (title) {
|
||||
strcat(songInfo, title);
|
||||
free(title);
|
||||
}
|
||||
ov_clear(&vf);
|
||||
filepstream = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
if (!songInfo) {
|
||||
/* If we didn't get any song info via tags or comments,
|
||||
then lets just use the filename */
|
||||
char *p1 = NULL;
|
||||
char *p2 = basename(fileName);
|
||||
if (p2) {
|
||||
songInfo = strdup(p2);
|
||||
p1 = strrchr(songInfo, '.');
|
||||
if (p1) {
|
||||
*p1 = '\000';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (songInfo) {
|
||||
shout_metadata_t *pmetadata = shout_metadata_new();
|
||||
shout_metadata_add(pmetadata, "song", songInfo);
|
||||
shout_set_metadata(shout, pmetadata);
|
||||
shout_metadata_free(pmetadata);
|
||||
}
|
||||
else {
|
||||
songInfo = strdup(blankString);
|
||||
}
|
||||
if (filepstream) {
|
||||
fclose(filepstream);
|
||||
}
|
||||
printf("Songinfo is (%s)\n", songInfo);
|
||||
return songInfo;
|
||||
}
|
||||
|
||||
FILE *openResource(shout_t *shout, char *fileName)
|
||||
{
|
||||
FILE *filep = NULL;
|
||||
|
||||
if (!strcmp(fileName, "stdin")) {
|
||||
#ifdef WIN32
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
#endif
|
||||
filep = stdin;
|
||||
}
|
||||
else {
|
||||
char extension[25];
|
||||
char *p1 = NULL;
|
||||
char *pMetadata = NULL;
|
||||
char *pCommandString = NULL;
|
||||
memset(extension, '\000', sizeof(extension));
|
||||
p1 = strrchr(fileName, '.');
|
||||
if (p1) {
|
||||
strncpy(extension, p1, sizeof(extension)-1);
|
||||
}
|
||||
|
||||
pMetadata = processMetadata(shout, extension, fileName);
|
||||
if (pezConfig->reencode) {
|
||||
/* Lets set the metadata first */
|
||||
if (strlen(extension) > 0) {
|
||||
pCommandString = buildCommandString(extension, fileName, pMetadata);
|
||||
/* Open up the decode/encode loop using popen() */
|
||||
filep = popen(pCommandString, "r");
|
||||
free(pMetadata);
|
||||
free(pCommandString);
|
||||
return filep;
|
||||
}
|
||||
else {
|
||||
printf("Cannot determine extension, don't know how to deal with (%s)\n", fileName);
|
||||
free(pMetadata);
|
||||
return NULL;
|
||||
}
|
||||
free(pMetadata);
|
||||
|
||||
}
|
||||
else {
|
||||
filep = fopen(fileName, "rb");
|
||||
return filep;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int streamFile(shout_t *shout, char *fileName) {
|
||||
FILE *filepstream = NULL;
|
||||
char buff[4096];
|
||||
long read, ret, total;
|
||||
ID3Tag id3tag;
|
||||
|
||||
|
||||
printf("Streaming %s\n", fileName);
|
||||
|
||||
if (!strcmp(pezConfig->fileName, "stdin")) {
|
||||
#ifdef WIN32
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
#endif
|
||||
filepstream = stdin;
|
||||
}
|
||||
else {
|
||||
filepstream = fopen(fileName, "rb");
|
||||
}
|
||||
filepstream = openResource(shout, fileName);
|
||||
if (!filepstream) {
|
||||
printf("Cannot open %s\n", fileName);
|
||||
return 0;
|
||||
}
|
||||
if (pezConfig->format == MP3_FORMAT) {
|
||||
/* Look for the ID3 tag */
|
||||
memset(&id3tag, '\000', sizeof(id3tag));
|
||||
fseek(filepstream, -128L, SEEK_END);
|
||||
fread(&id3tag, 1, sizeof(id3tag), filepstream);
|
||||
if (!strncmp(id3tag.tag, "TAG", strlen("TAG"))) {
|
||||
/* We have an Id3 tag */
|
||||
shout_metadata_t *pmetadata = shout_metadata_new();
|
||||
char songInfo[135] = "";
|
||||
sprintf(songInfo, "%s - %s", id3tag.artistName, id3tag.trackName);
|
||||
shout_metadata_add(pmetadata, "song", songInfo);
|
||||
shout_set_metadata(shout, pmetadata);
|
||||
shout_metadata_free(pmetadata);
|
||||
}
|
||||
rewind(filepstream);
|
||||
}
|
||||
total = 0;
|
||||
while (!feof(filepstream)) {
|
||||
read = fread(buff, 1, sizeof(buff), filepstream);
|
||||
@ -159,52 +367,52 @@ int streamPlaylist(shout_t *shout, char *fileName) {
|
||||
return(0);
|
||||
}
|
||||
while (loop) {
|
||||
while (!feof(filep)) {
|
||||
memset(streamFileName, '\000', sizeof(streamFileName));
|
||||
fgets(streamFileName, sizeof(streamFileName), filep);
|
||||
streamFileName[strlen(streamFileName)-1] = '\000';
|
||||
if (strlen(streamFileName) > 0) {
|
||||
memset(lastStreamFileName, '\000', sizeof(lastStreamFileName));
|
||||
strcpy(lastStreamFileName, streamFileName);
|
||||
/* Skip entries that begin with a # */
|
||||
if (strncmp(streamFileName, "#", 1)) {
|
||||
streamFile(shout, streamFileName);
|
||||
}
|
||||
}
|
||||
if (rereadPlaylist) {
|
||||
rereadPlaylist = 0;
|
||||
fclose(filep);
|
||||
printf("Reopening playlist\n");
|
||||
filep = fopen(fileName, "r");
|
||||
if (filep == 0) {
|
||||
printf("Cannot open %s\n", fileName);
|
||||
return(0);
|
||||
}
|
||||
else {
|
||||
int loop2 = 1;
|
||||
printf("Repositioning to (%s)\n", lastStreamFileName);
|
||||
while (loop2) {
|
||||
/* If we reach the end before finding
|
||||
our last spot, we will start over at the
|
||||
beginning */
|
||||
if (feof(filep)) {
|
||||
loop2 = 0;
|
||||
}
|
||||
else {
|
||||
memset(streamFileName, '\000', sizeof(streamFileName));
|
||||
fgets(streamFileName, sizeof(streamFileName), filep);
|
||||
streamFileName[strlen(streamFileName)-1] = '\000';
|
||||
if (!strcmp(streamFileName, lastStreamFileName)) {
|
||||
/* If we found our last position, then bump out of the loop */
|
||||
loop2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
while (!feof(filep)) {
|
||||
memset(streamFileName, '\000', sizeof(streamFileName));
|
||||
fgets(streamFileName, sizeof(streamFileName), filep);
|
||||
streamFileName[strlen(streamFileName)-1] = '\000';
|
||||
if (strlen(streamFileName) > 0) {
|
||||
memset(lastStreamFileName, '\000', sizeof(lastStreamFileName));
|
||||
strcpy(lastStreamFileName, streamFileName);
|
||||
/* Skip entries that begin with a # */
|
||||
if (strncmp(streamFileName, "#", 1)) {
|
||||
streamFile(shout, streamFileName);
|
||||
}
|
||||
}
|
||||
rewind(filep);
|
||||
if (rereadPlaylist) {
|
||||
rereadPlaylist = 0;
|
||||
fclose(filep);
|
||||
printf("Reopening playlist\n");
|
||||
filep = fopen(fileName, "r");
|
||||
if (filep == 0) {
|
||||
printf("Cannot open %s\n", fileName);
|
||||
return(0);
|
||||
}
|
||||
else {
|
||||
int loop2 = 1;
|
||||
printf("Repositioning to (%s)\n", lastStreamFileName);
|
||||
while (loop2) {
|
||||
/* If we reach the end before finding
|
||||
our last spot, we will start over at the
|
||||
beginning */
|
||||
if (feof(filep)) {
|
||||
loop2 = 0;
|
||||
}
|
||||
else {
|
||||
memset(streamFileName, '\000', sizeof(streamFileName));
|
||||
fgets(streamFileName, sizeof(streamFileName), filep);
|
||||
streamFileName[strlen(streamFileName)-1] = '\000';
|
||||
if (!strcmp(streamFileName, lastStreamFileName)) {
|
||||
/* If we found our last position, then bump out of the loop */
|
||||
loop2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
rewind(filep);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
@ -246,7 +454,7 @@ int main(int argc, char **argv)
|
||||
else {
|
||||
parseConfig(configFile);
|
||||
}
|
||||
|
||||
|
||||
if (pezConfig->URL) {
|
||||
host = (char *)malloc(strlen(pezConfig->URL) +1);
|
||||
memset(host, '\000', strlen(pezConfig->URL) +1);
|
||||
@ -277,7 +485,7 @@ int main(int argc, char **argv)
|
||||
usage();
|
||||
}
|
||||
if (pezConfig->format == 0) {
|
||||
printf("You must specify a format type of MP3 or OGGVORBIS\n");
|
||||
printf("You must specify a format type of MP3, VORBIS, or THEORA\n");
|
||||
}
|
||||
if (!(shout = shout_new())) {
|
||||
printf("Could not allocate shout_t\n");
|
||||
@ -314,13 +522,19 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pezConfig->format == MP3_FORMAT) {
|
||||
if (!strcmp(pezConfig->format, MP3_FORMAT)) {
|
||||
if (shout_set_format(shout, SHOUT_FORMAT_MP3) != SHOUTERR_SUCCESS) {
|
||||
printf("Error setting user: %s\n", shout_get_error(shout));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (pezConfig->format == OGG_FORMAT) {
|
||||
if (!strcmp(pezConfig->format, VORBIS_FORMAT)) {
|
||||
if (shout_set_format(shout, SHOUT_FORMAT_OGG) != SHOUTERR_SUCCESS) {
|
||||
printf("Error setting user: %s\n", shout_get_error(shout));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!strcmp(pezConfig->format, THEORA_FORMAT)) {
|
||||
if (shout_set_format(shout, SHOUT_FORMAT_OGG) != SHOUTERR_SUCCESS) {
|
||||
printf("Error setting user: %s\n", shout_get_error(shout));
|
||||
return 1;
|
||||
|
@ -66,7 +66,7 @@ LINK32=link.exe
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../libshout/include" /I "../src" /I "../../libxml2/include" /I "../../iconv/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../libshout/include" /I "../src" /I "../../libxml2/include" /I "../../iconv/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
@ -74,7 +74,7 @@ BSC32=bscmake.exe
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\..\libshout\win32\Debug\libshout.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib winmm.lib libxml2.lib iconv.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"../../libshout-2.0/win32/Debug" /libpath:"../../libxml2/lib" /libpath:"../../iconv/lib"
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\..\libshout\win32\Debug\libshout.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib winmm.lib libxml2.lib iconv.lib vorbisfile.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"../../oggvorbis-win32sdk-1.0.1/lib" /libpath:"../../libshout-2.0/win32/Debug" /libpath:"../../libxml2/lib" /libpath:"../../iconv/lib"
|
||||
|
||||
!ENDIF
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user