diff --git a/NEWS b/NEWS index e38f22a..3cdb405 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,8 @@ Changes in 0.3.0, (SVN trunk): the new configuration option to 1. - New configuration option, which makes ezstream play a media file or playlist once and then exit. + - New option to restrict the number of reconnection + attempts to a server in case the connection goes down. - Add feature to skip the currently streaming track, done by sending the SIGUSR1 signal to the ezstream process. - New command line option `-q': Suppress standard error output from external diff --git a/doc/ezstream.1.in b/doc/ezstream.1.in index 750cd45..1f9e630 100644 --- a/doc/ezstream.1.in +++ b/doc/ezstream.1.in @@ -198,6 +198,13 @@ to stream \& content only once, and to .Pq zero for continuous streaming .Pq the default . +.It Sy \& +Set how many attempts should be made to reconnect to the Icecast server in case +the connection is interrupted. +The default is to try indefinitely, which is equal to setting this +configuration option to +.Sy 0 +.Pq zero . .It Sy \& .Pq Optional. Set the name of the broadcast. diff --git a/src/configfile.c b/src/configfile.c index be509e0..25c68a0 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -140,8 +140,8 @@ parseConfig(const char *fileName) xmlDocPtr doc; xmlNodePtr cur; char *ls_xmlContentPtr; - int program_set, shuffle_set, streamOnce_set, - svrinfopublic_set; + int program_set, reconnect_set, shuffle_set, + streamOnce_set, svrinfopublic_set; xmlLineNumbersDefault(1); if ((doc = xmlParseFile(fileName)) == NULL) { @@ -160,6 +160,7 @@ parseConfig(const char *fileName) memset(&ezConfig, '\000', sizeof(ezConfig)); program_set = 0; + reconnect_set = 0; shuffle_set = 0; streamOnce_set = 0; svrinfopublic_set = 0; @@ -270,6 +271,19 @@ parseConfig(const char *fileName) streamOnce_set = 1; } } + if (!xmlStrcmp(cur->name, BAD_CAST "reconnect_tries")) { + if (reconnect_set) { + printf("%s[%ld]: Error: Cannot have multiple elements.\n", + fileName, xmlGetLineNo(cur)); + goto config_error; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.reconnectAttempts = atoi(ls_xmlContentPtr); + xmlFree(ls_xmlContentPtr); + reconnect_set = 1; + } + } if (!xmlStrcmp(cur->name, BAD_CAST "svrinfoname")) { if (ezConfig.serverName != NULL) { printf("%s[%ld]: Error: Cannot have multiple elements.\n", diff --git a/src/configfile.h b/src/configfile.h index f5c4b62..7b90976 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -59,6 +59,7 @@ typedef struct tag_EZCONFIG { int shuffle; int fileNameIsProgram; int streamOnce; + unsigned int reconnectAttempts; } EZCONFIG; EZCONFIG * getEZConfig(void); diff --git a/src/ezstream.c b/src/ezstream.c index b03a5cd..f785005 100644 --- a/src/ezstream.c +++ b/src/ezstream.c @@ -83,6 +83,11 @@ # define STRNCASECMP strncasecmp #endif /* WIN32 */ +#define STREAM_DONE 0 +#define STREAM_CONT 1 +#define STREAM_SKIP 2 +#define STREAM_SERVERR 3 + #ifdef HAVE___PROGNAME extern char *__progname; #else @@ -130,6 +135,7 @@ void setMetadata(shout_t *, const char *); char * buildCommandString(const char *, const char *, const char *); char * processMetadata(shout_t *, const char *, const char *); FILE * openResource(shout_t *, const char *, int *, char **, int *); +int reconnectServer(shout_t *, int); int sendStream(shout_t *, FILE *, const char *, int, void *); int streamFile(shout_t *, const char *); int streamPlaylist(shout_t *, const char *); @@ -565,13 +571,59 @@ openResource(shout_t *shout, const char *fileName, int *popenFlag, return (filep); } +int +reconnectServer(shout_t *shout, int closeConn) +{ + unsigned int i; + int close_conn = closeConn; + + printf("%s: Connection to %s lost.\n", __progname, pezConfig->URL); + + i = 0; + while (++i) { + printf("%s: Attempting reconnection #", __progname); + if (pezConfig->reconnectAttempts > 0) + printf("%u/%u: ", i, + pezConfig->reconnectAttempts); + else + printf("%u: ", i); + + if (close_conn == 0) + close_conn = 1; + else + shout_close(shout); + if (shout_open(shout) == SHOUTERR_SUCCESS) { + printf("OK\n%s: Reconnect to %s successful.\n", + __progname, pezConfig->URL); + return (1); + } + + printf("FAILED: %s\n", shout_get_error(shout)); + + if (pezConfig->reconnectAttempts > 0 && + i >= pezConfig->reconnectAttempts) + break; + + printf("%s: Waiting 5s for %s to come back ...\n", + __progname, pezConfig->URL); +#ifdef WIN32 + Sleep(5000); +#else + sleep(5); +#endif + }; + + printf("%s: Giving up.\n", __progname); + return (0); +} + int sendStream(shout_t *shout, FILE *filepstream, const char *fileName, int isStdin, void *tv) { unsigned char buff[4096]; size_t read, total, oldTotal; - int ret = 0; + int ret; #ifdef HAVE_GETTIMEOFDAY double kbps = -1.0; struct timeval timeStamp, *startTime = (struct timeval *)tv; @@ -587,7 +639,14 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName, #endif /* HAVE_GETTIMEOFDAY */ total = oldTotal = 0; + ret = STREAM_DONE; while ((read = fread(buff, 1, sizeof(buff), filepstream)) > 0) { + if (shout_get_connected(shout) != SHOUTERR_CONNECTED && + reconnectServer(shout, 0) == 0) { + ret = STREAM_SERVERR; + break; + } + if (rereadPlaylist_notify) { rereadPlaylist_notify = 0; if (!pezConfig->fileNameIsProgram) @@ -596,7 +655,7 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName, } if (skipTrack) { skipTrack = 0; - ret = 2; + ret = STREAM_SKIP; break; } @@ -605,27 +664,11 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName, if (shout_send(shout, buff, read) != SHOUTERR_SUCCESS) { printf("%s: shout_send(): %s\n", __progname, shout_get_error(shout)); - while (1) { - printf("%s: Disconnected from server, reconnecting ...\n", - __progname); - shout_close(shout); - if (shout_open(shout) == SHOUTERR_SUCCESS) { - printf("%s: Reconnect to server successful.\n", - __progname); - if (shout_send(shout, buff, read) == SHOUTERR_SUCCESS) - break; - printf("%s: shout_send(): %s\n", - __progname, - shout_get_error(shout)); - } else { - printf("%s: Reconnect failed. Waiting 5 seconds ...\n", - __progname); -#ifdef WIN32 - Sleep(5000); -#else - sleep(5); -#endif - } + if (reconnectServer(shout, 1)) + break; + else { + ret = STREAM_SERVERR; + break; } } @@ -680,7 +723,7 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName, if (ferror(filepstream)) { if (errno == EINTR) { clearerr(filepstream); - ret = 1; + ret = STREAM_CONT; } else printf("%s: streamFile(): Error while reading '%s': %s\n", __progname, fileName, strerror(errno)); @@ -725,18 +768,22 @@ streamFile(shout_t *shout, const char *fileName) do { ret = sendStream(shout, filepstream, fileName, isStdin, NULL); #endif - if (ret != 0) { + if (ret != STREAM_DONE) { if (skipTrack && rereadPlaylist) { skipTrack = 0; ret = 1; } - if (ret == 2 || skipTrack) { + if (ret == STREAM_SKIP || skipTrack) { skipTrack = 0; if (!isStdin && vFlag) printf("%s: SIGUSR1 signal received, skipping current track.\n", __progname); retval = 1; - ret = 0; + ret = STREAM_DONE; + } + if (ret == STREAM_SERVERR) { + retval = 0; + ret = STREAM_DONE; } } else retval = 1;