2007-02-24 19:25:07 -05:00
|
|
|
/*
|
|
|
|
* ezstream - source client for Icecast with external en-/decoder support
|
|
|
|
* Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski <oddsock@oddsock.org>
|
|
|
|
* Copyright (C) 2007 Moritz Grimm <gtgbr@gmx.net>
|
|
|
|
*
|
|
|
|
* 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
|
2004-01-30 12:19:45 -05:00
|
|
|
|
2007-02-24 19:25:07 -05:00
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
|
|
# include <sys/types.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TIME_H
|
|
|
|
# include <sys/time.h>
|
|
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#ifdef HAVE_PATHS_H
|
|
|
|
# include <paths.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SIGNAL_H
|
|
|
|
# include <signal.h>
|
|
|
|
#endif
|
2004-01-30 12:19:45 -05:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2007-02-24 19:25:07 -05:00
|
|
|
#include <limits.h>
|
2004-02-01 23:37:42 -05:00
|
|
|
#ifdef WIN32
|
2007-02-24 19:25:07 -05:00
|
|
|
# include <io.h>
|
|
|
|
# include <windows.h>
|
|
|
|
#else
|
|
|
|
# include <libgen.h>
|
|
|
|
# include <unistd.h>
|
|
|
|
#endif /* WIN32 */
|
2004-01-30 12:19:45 -05:00
|
|
|
#include <shout/shout.h>
|
2007-02-24 19:25:07 -05:00
|
|
|
#include <vorbis/vorbisfile.h>
|
|
|
|
|
|
|
|
#ifndef HAVE_GETOPT
|
|
|
|
# include "getopt.h"
|
|
|
|
#endif
|
|
|
|
#if !defined(HAVE_STRLCAT) || !defined(HAVE_STRLCPY)
|
|
|
|
# include "strlfctns.h"
|
|
|
|
#endif
|
2004-01-30 12:19:45 -05:00
|
|
|
#include "configfile.h"
|
2007-02-24 19:25:07 -05:00
|
|
|
#include "playlist.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
#ifndef PATH_MAX
|
|
|
|
# define PATH_MAX 256
|
2004-07-18 23:12:31 -04:00
|
|
|
#endif
|
2007-02-24 19:25:07 -05:00
|
|
|
|
|
|
|
/* For Solaris, possibly others (usually defined in <paths.h>.) */
|
|
|
|
#ifndef _PATH_DEVNULL
|
|
|
|
# define _PATH_DEVNULL "/dev/null"
|
|
|
|
#endif /* _PATH_DEVNULL */
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
# define STRNCASECMP strnicmp
|
|
|
|
# define popen _popen
|
|
|
|
# define pclose _pclose
|
|
|
|
# define snprintf _snprintf
|
|
|
|
# define stat _stat
|
|
|
|
#else
|
|
|
|
# define STRNCASECMP strncasecmp
|
|
|
|
#endif /* WIN32 */
|
|
|
|
|
|
|
|
#ifdef HAVE___PROGNAME
|
|
|
|
extern char *__progname;
|
|
|
|
#else
|
|
|
|
char *__progname;
|
|
|
|
#endif /* HAVE___PROGNAME */
|
|
|
|
|
|
|
|
int qFlag;
|
|
|
|
int vFlag;
|
2004-01-30 12:19:45 -05:00
|
|
|
|
|
|
|
EZCONFIG *pezConfig = NULL;
|
2004-07-18 23:12:31 -04:00
|
|
|
static char *blankString = "";
|
2007-02-24 19:25:07 -05:00
|
|
|
playlist_t *playlist = NULL;
|
|
|
|
int playlistMode = 0;
|
2004-04-21 09:48:22 -04:00
|
|
|
|
2007-02-24 19:25:07 -05:00
|
|
|
#ifdef HAVE_SIGNALS
|
|
|
|
volatile sig_atomic_t rereadPlaylist = 0;
|
|
|
|
volatile sig_atomic_t rereadPlaylist_notify = 0;
|
|
|
|
volatile sig_atomic_t skipTrack = 0;
|
2004-04-21 09:48:22 -04:00
|
|
|
|
2007-02-24 19:25:07 -05:00
|
|
|
void
|
|
|
|
sig_handler(int sig)
|
2004-04-21 09:48:22 -04:00
|
|
|
{
|
2007-02-24 19:25:07 -05:00
|
|
|
switch (sig) {
|
|
|
|
case SIGHUP:
|
|
|
|
rereadPlaylist = 1;
|
|
|
|
rereadPlaylist_notify = 1;
|
|
|
|
break;
|
|
|
|
case SIGUSR1:
|
|
|
|
skipTrack = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2004-04-21 09:48:22 -04:00
|
|
|
}
|
2004-07-18 23:12:31 -04:00
|
|
|
#else
|
2007-02-24 19:25:07 -05:00
|
|
|
int rereadPlaylist = 0;
|
|
|
|
int rereadPlaylist_notify = 0;
|
|
|
|
int skipTrack = 0;
|
|
|
|
#endif /* HAVE_SIGNALS */
|
2004-01-30 12:19:45 -05:00
|
|
|
|
|
|
|
typedef struct tag_ID3Tag {
|
|
|
|
char tag[3];
|
|
|
|
char trackName[30];
|
|
|
|
char artistName[30];
|
|
|
|
char albumName[30];
|
|
|
|
char year[3];
|
|
|
|
char comment[30];
|
|
|
|
char genre;
|
|
|
|
} ID3Tag;
|
|
|
|
|
2007-02-24 19:25:07 -05:00
|
|
|
#ifdef WIN32
|
|
|
|
char * basename(const char *);
|
|
|
|
#endif
|
|
|
|
int strrcmp(const char *, const char *);
|
|
|
|
char * getProgname(const char *);
|
|
|
|
void usage(void);
|
|
|
|
void usageHelp(void);
|
2004-01-30 12:19:45 -05:00
|
|
|
|
2007-02-24 19:25:07 -05:00
|
|
|
#ifdef WIN32
|
|
|
|
char *
|
|
|
|
basename(const char *fileName)
|
2004-01-30 12:19:45 -05:00
|
|
|
{
|
2007-02-24 19:25:07 -05:00
|
|
|
char *pLast = strrchr(fileName, '\\');
|
2004-01-30 12:19:45 -05:00
|
|
|
|
2007-02-24 19:25:07 -05:00
|
|
|
if (pLast != NULL)
|
|
|
|
return (pLast + 1);
|
|
|
|
|
|
|
|
return (NULL);
|
2004-01-30 12:19:45 -05:00
|
|
|
}
|
2007-02-24 19:25:07 -05:00
|
|
|
#endif /* WIN32 */
|
|
|
|
|
|
|
|
int
|
|
|
|
strrcmp(const char *s, const char *sub)
|
|
|
|
{
|
|
|
|
int slen = strlen(s);
|
|
|
|
int sublen = strlen(sub);
|
|
|
|
|
|
|
|
if (sublen > slen)
|
|
|
|
return (1);
|
2004-01-30 12:19:45 -05:00
|
|
|
|
2007-02-24 19:25:07 -05:00
|
|
|
return (memcmp(s + slen - sublen, sub, sublen));
|
|
|
|
}
|
2004-01-30 12:19:45 -05:00
|
|
|
|
|
|
|
int urlParse(char *url, char *hostname, int *port, char *mountname)
|
|
|
|
{
|
|
|
|
char *p1;
|
|
|
|
char *p2;
|
|
|
|
char *p3;
|
|
|
|
char tmpPort[25] = "";
|
|
|
|
|
|
|
|
if (strncmp(url, "http://", strlen("http://"))) {
|
|
|
|
printf("Invalid URL, must be of the form http://server:port/mountpoint\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
p1 = url + strlen("http://");
|
|
|
|
p2 = strchr(p1, ':');
|
|
|
|
if (!p2) {
|
|
|
|
printf("Invalid URL, must be of the form http://server:port/mountpoint\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
strncpy(hostname, p1, p2-p1);
|
|
|
|
p2++;
|
|
|
|
p3 = strchr(p2, '/');
|
|
|
|
if (!p3) {
|
|
|
|
printf("Invalid URL, must be of the form http://server:port/mountpoint\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
memset(tmpPort, '\000', sizeof(tmpPort));
|
|
|
|
strncpy(tmpPort, p2, p3-p2);
|
|
|
|
*port = atoi(tmpPort);
|
|
|
|
strcpy(mountname, p3);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-02-24 19:25:07 -05:00
|
|
|
void replaceString(char *source, char *dest, char *from, char *to)
|
2004-07-18 23:12:31 -04:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
newDecoderLen = strlen(decoder) + strlen(fileName) + 1;
|
|
|
|
newDecoder = (char *)malloc(newDecoderLen);
|
|
|
|
memset(newDecoder, '\000', newDecoderLen);
|
2007-02-24 19:25:07 -05:00
|
|
|
replaceString(decoder, newDecoder, "@T@", fileName);
|
2004-07-18 23:12:31 -04:00
|
|
|
|
2004-12-21 20:49:56 -05:00
|
|
|
encoder = strdup(getFormatEncoder(pezConfig->format));
|
|
|
|
if (strlen(encoder) == 0) {
|
|
|
|
printf("Unknown format %s, passing right on through!\n", pezConfig->format);
|
|
|
|
commandStringLen = strlen(newDecoder) + 1;
|
|
|
|
commandString = (char *)malloc(commandStringLen);
|
|
|
|
memset(commandString, '\000', commandStringLen);
|
|
|
|
sprintf(commandString, "%s", newDecoder);
|
|
|
|
if (decoder) {
|
|
|
|
free(decoder);
|
|
|
|
}
|
|
|
|
if (encoder) {
|
|
|
|
free(encoder);
|
|
|
|
}
|
|
|
|
return commandString;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
newEncoderLen = strlen(encoder) + strlen(metadata) + 1;
|
|
|
|
newEncoder = (char *)malloc(newEncoderLen);
|
|
|
|
memset(newEncoder, '\000', newEncoderLen);
|
2007-02-24 19:25:07 -05:00
|
|
|
replaceString(encoder, newEncoder, "@M@", metadata);
|
2004-07-18 23:12:31 -04:00
|
|
|
|
2004-12-21 20:49:56 -05:00
|
|
|
commandStringLen = strlen(newDecoder) + strlen(" | ") + strlen(newEncoder) + 1;
|
|
|
|
commandString = (char *)malloc(commandStringLen);
|
|
|
|
memset(commandString, '\000', commandStringLen);
|
|
|
|
sprintf(commandString, "%s | %s", newDecoder, newEncoder);
|
|
|
|
}
|
|
|
|
if (decoder) {
|
|
|
|
free(decoder);
|
|
|
|
}
|
|
|
|
if (encoder) {
|
|
|
|
free(encoder);
|
|
|
|
}
|
2004-07-18 23:12:31 -04:00
|
|
|
printf("Going to execute (%s)\n", commandString);
|
|
|
|
return(commandString);
|
|
|
|
}
|
2005-12-14 16:13:25 -05:00
|
|
|
|
2004-07-18 23:12:31 -04:00
|
|
|
char * processMetadata(shout_t *shout, char *extension, char *fileName) {
|
2004-01-30 12:19:45 -05:00
|
|
|
FILE *filepstream = NULL;
|
2004-07-18 23:12:31 -04:00
|
|
|
char *artist = NULL;
|
|
|
|
char *title = NULL;
|
|
|
|
char *songInfo = NULL;
|
|
|
|
int songLen = 0;
|
2004-01-30 12:19:45 -05:00
|
|
|
ID3Tag id3tag;
|
2005-12-14 16:13:25 -05:00
|
|
|
char temptrackName[31];
|
|
|
|
char tempartistName[31];
|
2004-07-18 23:12:31 -04:00
|
|
|
|
|
|
|
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 */
|
2005-12-14 16:13:25 -05:00
|
|
|
memset(temptrackName, '\000', sizeof(temptrackName));
|
|
|
|
memset(tempartistName, '\000', sizeof(tempartistName));
|
|
|
|
snprintf(temptrackName, sizeof(temptrackName)-1, "%s", id3tag.trackName);
|
|
|
|
snprintf(tempartistName, sizeof(tempartistName)-1, "%s", id3tag.artistName);
|
|
|
|
|
|
|
|
songLen = sizeof(tempartistName) + strlen(" - ") + sizeof(temptrackName) + 1;
|
2004-07-18 23:12:31 -04:00
|
|
|
songInfo = (char *)malloc(songLen);
|
|
|
|
memset(songInfo, '\000', songLen);
|
|
|
|
|
2005-12-14 16:13:25 -05:00
|
|
|
snprintf(songInfo, songLen-1, "%s - %s", tempartistName, temptrackName);
|
2004-07-18 23:12:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 */
|
2005-12-14 16:13:25 -05:00
|
|
|
char *p1 = NULL;
|
|
|
|
char *p2 = basename(fileName);
|
2004-07-18 23:12:31 -04:00
|
|
|
if (p2) {
|
|
|
|
songInfo = strdup(p2);
|
|
|
|
p1 = strrchr(songInfo, '.');
|
|
|
|
if (p1) {
|
|
|
|
*p1 = '\000';
|
2005-12-14 16:13:25 -05:00
|
|
|
}
|
2004-07-18 23:12:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-12-14 16:13:25 -05:00
|
|
|
FILE *openResource(shout_t *shout, char *fileName, int *popenFlag)
|
2004-07-18 23:12:31 -04:00
|
|
|
{
|
|
|
|
FILE *filep = NULL;
|
2005-12-14 16:13:25 -05:00
|
|
|
|
|
|
|
printf("Opening file (%s)\n", fileName);
|
2004-07-18 23:12:31 -04:00
|
|
|
if (!strcmp(fileName, "stdin")) {
|
2004-02-01 23:37:42 -05:00
|
|
|
#ifdef WIN32
|
|
|
|
_setmode(_fileno(stdin), _O_BINARY);
|
|
|
|
#endif
|
2004-07-18 23:12:31 -04:00
|
|
|
filep = stdin;
|
2004-12-21 20:49:56 -05:00
|
|
|
return filep;
|
2004-01-30 12:19:45 -05:00
|
|
|
}
|
|
|
|
else {
|
2004-07-18 23:12:31 -04:00
|
|
|
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);
|
2005-12-14 16:13:25 -05:00
|
|
|
*popenFlag = 0;
|
2004-07-18 23:12:31 -04:00
|
|
|
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() */
|
2005-12-14 16:13:25 -05:00
|
|
|
filep = popen(pCommandString, "r");
|
|
|
|
*popenFlag = 1;
|
|
|
|
#ifdef WIN32
|
|
|
|
_setmode(_fileno(filep), _O_BINARY );
|
|
|
|
#endif
|
2004-07-18 23:12:31 -04:00
|
|
|
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;
|
|
|
|
}
|
2004-01-30 12:19:45 -05:00
|
|
|
}
|
2004-07-18 23:12:31 -04:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int streamFile(shout_t *shout, char *fileName) {
|
|
|
|
FILE *filepstream = NULL;
|
|
|
|
char buff[4096];
|
2005-12-14 16:13:25 -05:00
|
|
|
long read, ret = 0, total;
|
|
|
|
int popenFlag = 0;
|
2004-07-18 23:12:31 -04:00
|
|
|
|
|
|
|
|
|
|
|
printf("Streaming %s\n", fileName);
|
|
|
|
|
2005-12-14 16:13:25 -05:00
|
|
|
filepstream = openResource(shout, fileName, &popenFlag);
|
2004-01-30 12:19:45 -05:00
|
|
|
if (!filepstream) {
|
|
|
|
printf("Cannot open %s\n", fileName);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
total = 0;
|
|
|
|
while (!feof(filepstream)) {
|
|
|
|
read = fread(buff, 1, sizeof(buff), filepstream);
|
|
|
|
total = total + read;
|
|
|
|
|
|
|
|
if (read > 0) {
|
|
|
|
ret = shout_send(shout, buff, read);
|
2005-12-14 16:13:25 -05:00
|
|
|
if (ret != SHOUTERR_SUCCESS) {
|
2005-01-04 19:38:09 -05:00
|
|
|
int loop = 1;
|
2005-12-14 16:13:25 -05:00
|
|
|
printf("DEBUG: Send error: %s\n", shout_get_error(shout));
|
|
|
|
|
|
|
|
while (loop) {
|
|
|
|
printf("Disconnected from server, reconnecting....\n");
|
|
|
|
shout_close(shout);
|
|
|
|
if (shout_open(shout) == SHOUTERR_SUCCESS) {
|
|
|
|
printf("Successful reconnection....\n");
|
|
|
|
ret = shout_send(shout, buff, read);
|
|
|
|
loop = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
printf("Reconnect failed..waiting 5 seconds.\n");
|
|
|
|
#ifdef WIN32
|
|
|
|
Sleep(5000);
|
|
|
|
#else
|
|
|
|
sleep(5);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
2004-01-30 12:19:45 -05:00
|
|
|
}
|
|
|
|
shout_delay(shout);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
shout_sync(shout);
|
|
|
|
}
|
2005-12-14 16:13:25 -05:00
|
|
|
if (popenFlag) {
|
|
|
|
printf("Closing via pclose\n");
|
|
|
|
pclose(filepstream);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fclose(filepstream);
|
|
|
|
}
|
2004-01-30 12:19:45 -05:00
|
|
|
filepstream = NULL;
|
2005-01-04 19:38:09 -05:00
|
|
|
return ret;
|
2004-01-30 12:19:45 -05:00
|
|
|
}
|
2007-02-24 19:25:07 -05:00
|
|
|
|
2004-01-30 12:19:45 -05:00
|
|
|
int streamPlaylist(shout_t *shout, char *fileName) {
|
|
|
|
FILE *filep = NULL;
|
|
|
|
char streamFileName[8096] = "";
|
2004-04-21 09:48:22 -04:00
|
|
|
char lastStreamFileName[8096] = "";
|
2005-12-14 16:13:25 -05:00
|
|
|
int loop = 1;
|
2004-01-30 12:19:45 -05:00
|
|
|
|
|
|
|
filep = fopen(fileName, "r");
|
|
|
|
if (filep == 0) {
|
|
|
|
printf("Cannot open %s\n", fileName);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
while (loop) {
|
2004-07-18 23:12:31 -04:00
|
|
|
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)) {
|
2005-12-14 16:13:25 -05:00
|
|
|
streamFile(shout, streamFileName);
|
2004-01-30 12:19:45 -05:00
|
|
|
}
|
2004-07-18 23:12:31 -04:00
|
|
|
}
|
|
|
|
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 */
|
2004-04-21 09:48:22 -04:00
|
|
|
loop2 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-07-18 23:12:31 -04:00
|
|
|
|
2004-04-21 09:48:22 -04:00
|
|
|
}
|
2004-01-30 12:19:45 -05:00
|
|
|
}
|
2004-07-18 23:12:31 -04:00
|
|
|
}
|
|
|
|
rewind(filep);
|
2004-01-30 12:19:45 -05:00
|
|
|
}
|
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
|
2007-02-24 19:25:07 -05:00
|
|
|
/* Borrowed from OpenNTPd-portable's compat-openbsd/bsd-misc.c */
|
|
|
|
char *
|
|
|
|
getProgname(const char *argv0)
|
|
|
|
{
|
|
|
|
#ifdef HAVE___PROGNAME
|
|
|
|
return (xstrdup(__progname));
|
|
|
|
#else
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if (argv0 == NULL)
|
|
|
|
return ((char *)"ezstream");
|
|
|
|
p = strrchr(argv0, '/');
|
|
|
|
if (p == NULL)
|
|
|
|
p = argv0;
|
|
|
|
else
|
|
|
|
p++;
|
|
|
|
|
|
|
|
return (xstrdup(p));
|
|
|
|
#endif /* HAVE___PROGNAME */
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usage(void)
|
|
|
|
{
|
|
|
|
printf("usage: %s [-hqv] [-c configfile]\n", __progname);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usageHelp(void)
|
|
|
|
{
|
|
|
|
printf("\n");
|
|
|
|
printf(" -c configfile use XML configuration in configfile\n");
|
|
|
|
printf(" -h display this additional help and exit\n");
|
|
|
|
printf(" -q suppress STDERR output from external en-/decoders\n");
|
|
|
|
printf(" -v verbose output\n");
|
|
|
|
printf("\n");
|
|
|
|
printf("See the ezstream(1) manual for detailed information.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
2004-01-30 12:19:45 -05:00
|
|
|
{
|
|
|
|
char c;
|
|
|
|
char *configFile = NULL;
|
|
|
|
char *host = NULL;
|
|
|
|
int port = 0;
|
|
|
|
char *mount = NULL;
|
|
|
|
shout_t *shout;
|
|
|
|
|
|
|
|
|
|
|
|
pezConfig = getEZConfig();
|
2004-04-21 09:48:22 -04:00
|
|
|
#ifndef WIN32
|
2007-02-24 19:25:07 -05:00
|
|
|
signal(SIGHUP, sig_handler);
|
2004-04-21 09:48:22 -04:00
|
|
|
#endif
|
2004-01-30 12:19:45 -05:00
|
|
|
|
|
|
|
shout_init();
|
|
|
|
|
|
|
|
while ((c = getopt(argc, argv, "hc:")) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 'c':
|
|
|
|
configFile = optarg;
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
usage();
|
2007-02-24 19:25:07 -05:00
|
|
|
usageHelp();
|
|
|
|
return (0);
|
2004-01-30 12:19:45 -05:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!configFile) {
|
|
|
|
printf("You must supply a config file\n");
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
parseConfig(configFile);
|
|
|
|
}
|
2004-07-18 23:12:31 -04:00
|
|
|
|
2004-01-30 12:19:45 -05:00
|
|
|
if (pezConfig->URL) {
|
|
|
|
host = (char *)malloc(strlen(pezConfig->URL) +1);
|
|
|
|
memset(host, '\000', strlen(pezConfig->URL) +1);
|
|
|
|
mount = (char *)malloc(strlen(pezConfig->URL) +1);
|
|
|
|
memset(mount, '\000', strlen(pezConfig->URL) +1);
|
|
|
|
if (!urlParse(pezConfig->URL, host, &port, mount)) {
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((host == NULL)) {
|
|
|
|
printf("server is required\n");
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if ((port == 0)) {
|
|
|
|
printf("port is required\n");
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if ((pezConfig->password == NULL)) {
|
|
|
|
printf("-p password is required\n");
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if ((mount == NULL)) {
|
|
|
|
printf("mountpoint is required\n");
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if ((pezConfig->fileName == NULL)) {
|
|
|
|
printf("-f fileName is required\n");
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if (pezConfig->format == 0) {
|
2004-07-18 23:12:31 -04:00
|
|
|
printf("You must specify a format type of MP3, VORBIS, or THEORA\n");
|
2004-01-30 12:19:45 -05:00
|
|
|
}
|
|
|
|
if (!(shout = shout_new())) {
|
|
|
|
printf("Could not allocate shout_t\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (shout_set_host(shout, host) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting hostname: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shout_set_protocol(shout, SHOUT_PROTOCOL_HTTP) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting protocol: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shout_set_port(shout, port) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting port: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shout_set_password(shout, pezConfig->password) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting password: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (shout_set_mount(shout, mount) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting mount: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shout_set_user(shout, "source") != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting user: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2004-07-18 23:12:31 -04:00
|
|
|
if (!strcmp(pezConfig->format, MP3_FORMAT)) {
|
2004-01-30 12:19:45 -05:00
|
|
|
if (shout_set_format(shout, SHOUT_FORMAT_MP3) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting user: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2004-07-18 23:12:31 -04:00
|
|
|
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)) {
|
2004-07-12 15:13:14 -04:00
|
|
|
if (shout_set_format(shout, SHOUT_FORMAT_OGG) != SHOUTERR_SUCCESS) {
|
2004-01-30 12:19:45 -05:00
|
|
|
printf("Error setting user: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pezConfig->serverName) {
|
|
|
|
if (shout_set_name(shout, pezConfig->serverName) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting server name: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pezConfig->serverURL) {
|
|
|
|
if (shout_set_url(shout, pezConfig->serverURL) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting server url: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pezConfig->serverGenre) {
|
|
|
|
if (shout_set_genre(shout, pezConfig->serverGenre) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting server genre: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pezConfig->serverDescription) {
|
|
|
|
if (shout_set_description(shout, pezConfig->serverDescription) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting server description: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pezConfig->serverBitrate) {
|
|
|
|
if (shout_set_audio_info(shout, SHOUT_AI_BITRATE, pezConfig->serverBitrate) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting server bitrate: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pezConfig->serverChannels) {
|
|
|
|
if (shout_set_audio_info(shout, SHOUT_AI_CHANNELS, pezConfig->serverChannels) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting server channels: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pezConfig->serverSamplerate) {
|
|
|
|
if (shout_set_audio_info(shout, SHOUT_AI_SAMPLERATE, pezConfig->serverSamplerate) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting server samplerate: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pezConfig->serverQuality) {
|
|
|
|
if (shout_set_audio_info(shout, SHOUT_AI_QUALITY, pezConfig->serverQuality) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting server quality: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shout_set_public(shout, pezConfig->serverPublic) != SHOUTERR_SUCCESS) {
|
|
|
|
printf("Error setting server public flag: %s\n", shout_get_error(shout));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Connecting to %s...", pezConfig->URL);
|
|
|
|
if (shout_open(shout) == SHOUTERR_SUCCESS) {
|
|
|
|
printf("SUCCESS.\n");
|
|
|
|
while (1) {
|
|
|
|
if (!strrcmp(pezConfig->fileName, ".m3u")) {
|
|
|
|
streamPlaylist(shout, pezConfig->fileName);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
streamFile(shout, pezConfig->fileName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("FAILED: %s\n", shout_get_error(shout));
|
|
|
|
}
|
|
|
|
|
|
|
|
shout_close(shout);
|
|
|
|
|
|
|
|
shout_shutdown();
|
|
|
|
|
|
|
|
if (host) {
|
|
|
|
free(host);
|
|
|
|
}
|
|
|
|
if (mount) {
|
|
|
|
free(mount);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|