--- mpg321-0.2.10/mad.c 2002-03-23 23:49:31.000000000 -0600 +++ mpg321-0.2.10.dev/mad.c 2004-01-03 10:52:35.000000000 -0600 @@ -40,6 +40,13 @@ #include "mpg321.h" unsigned long current_frame=0; +unsigned long int icy_buf_read; /* bytes read while looking for tag */ +int icy_tag_crossed_boundary; /* did meta tag cross packet? */ +static char tag_buf[255 * 16]; /* max size of icecast tags */ + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif enum mad_flow read_from_mmap(void *data, struct mad_stream *stream) { @@ -80,12 +87,68 @@ return MAD_FLOW_CONTINUE; } +static void print_icecast_stream_title (void) +{ + char *stream_title, *end_quote, *artist, *title; + char emptystring[31], artist_string[31], title_string[31]; + + tag_buf[sizeof(tag_buf)-1] = '\0'; + memset (emptystring, ' ', 30); + emptystring[30] = artist_string[30] = title_string[30] = '\0'; + + if (stream_title = strstr(tag_buf, "StreamTitle='")) + { + stream_title += strlen("StreamTitle='"); + if (end_quote = strstr(stream_title, "';")) + { + *end_quote = '\0'; +#if 0 + printf("@I %s\n", stream_title); /* for debugging */ +#endif + /* try to find "artist - title" */ + if (title = strchr(artist = stream_title, '-')) + { + char *tail = title-1; + + *title++= '\0'; + while (*title == ' ') title++; + while ((tail > artist) && (*tail == ' ')) *tail-- = '\0'; + } + /* try to find "(artist) title" */ + else if ((artist = strchr(stream_title, '(')) < + (title = strchr(stream_title, ')')) ) + { + artist++; + *title++ = '\0'; + while (*title == ' ') title++; + } + /* punt */ + else { + title = stream_title; + artist = ""; + } + snprintf(title_string, 31, "%-30s", title); + snprintf(artist_string, 31, "%-30s", artist); + printf("@I ID3:%s%s%s%s%s%s\n", title_string, artist_string, + emptystring, /*year*/ " ", emptystring, emptystring); + + } + } +} + /* assumes that playbuf->buf has been preallocated to BUF_SIZE */ enum mad_flow read_from_fd(void *data, struct mad_stream *stream) { buffer *playbuf = data; int bytes_to_preserve = stream->bufend - stream->next_frame; - + int readbytes; + static int tag_remain; + static int tag_consumed; + int meta_consumed; + int bytes_after_tag, i; + char *meta_start, *tag_start, *buf_end; + + if(playbuf->done) { status = MPG321_STOPPED; @@ -105,14 +168,86 @@ /* need to preserve the bytes which comprise an incomplete frame, that mad has passed back to us */ - if (bytes_to_preserve) + if (bytes_to_preserve) { memmove(playbuf->buf, stream->next_frame, bytes_to_preserve); + } + + if (icy_metaint) + { + readbytes = read(playbuf->fd, playbuf->buf + bytes_to_preserve, + min((icy_metaint),BUF_SIZE) - bytes_to_preserve); + icy_buf_read += readbytes; + } else { + readbytes = read(playbuf->fd, playbuf->buf + bytes_to_preserve, + BUF_SIZE - bytes_to_preserve); + } - if( !(read(playbuf->fd, playbuf->buf + bytes_to_preserve, BUF_SIZE - bytes_to_preserve) > 0) ) + if (readbytes <= 0) + { playbuf->done = 1; + return MAD_FLOW_CONTINUE; + } - mad_stream_buffer(stream, playbuf->buf, playbuf->length); - + if (icy_metaint) + { + buf_end = playbuf->buf + bytes_to_preserve + readbytes; + meta_consumed = 0; + + if (icy_buf_read > icy_metaint) + { + meta_start = buf_end - (icy_buf_read - icy_metaint); + tag_remain = ((unsigned char) *meta_start) * 16; + tag_consumed = 0; + tag_start = meta_start + 1; + meta_consumed = 1; /* consume size byte */ + + if (tag_remain > 0) + { + if (tag_start < buf_end) + { + /* get as much tag data as we can */ + tag_consumed = min(tag_remain, buf_end - tag_start); + tag_remain -= tag_consumed; + meta_consumed += tag_consumed; + memmove(tag_buf, tag_start, tag_consumed); + + if (tag_remain) + { + icy_tag_crossed_boundary=1; + } else { + print_icecast_stream_title(); + } + } + else + { + icy_tag_crossed_boundary=1; + } + } + } + else if (icy_tag_crossed_boundary) + { + meta_start = tag_start = playbuf->buf + bytes_to_preserve; + i = tag_consumed; + tag_consumed = min(tag_remain, buf_end - tag_start); + meta_consumed = tag_consumed; + memmove(tag_buf+i, tag_start, tag_consumed); /* copy tag */ + tag_remain -= tag_consumed; + if (tag_remain == 0) + { + print_icecast_stream_title(); + icy_tag_crossed_boundary = 0; + } + } + + if (meta_consumed) { + readbytes -= meta_consumed; + bytes_after_tag = buf_end - (meta_start + meta_consumed); + icy_buf_read = bytes_after_tag; + memmove(meta_start, meta_start+meta_consumed, bytes_after_tag); + } + } + + mad_stream_buffer(stream, playbuf->buf, readbytes + bytes_to_preserve); return MAD_FLOW_CONTINUE; } --- mpg321-0.2.10/mpg321.h 2002-03-23 16:45:31.000000000 -0600 +++ mpg321-0.2.10.dev/mpg321.h 2003-11-28 00:18:53.000000000 -0600 @@ -109,6 +109,9 @@ extern int quit_now; extern char remote_input_buf[PATH_MAX + 5]; extern int file_change; +extern int icy_metaint; +extern unsigned long int icy_buf_read; +extern int icy_tag_crossed_boundary; extern int status; @@ -148,6 +151,7 @@ /* playlist functions */ playlist * new_playlist(); +void delete_playlist(playlist *pl); void resize_playlist(playlist *pl); char * get_next_file(playlist *pl, buffer *buf); void add_cmdline_files(playlist *pl, char *argv[]); --- mpg321-0.2.10/network.c 2002-03-23 23:49:56.000000000 -0600 +++ mpg321-0.2.10.dev/network.c 2004-01-02 22:57:35.000000000 -0600 @@ -40,8 +40,16 @@ #include +#include +#include + #include "mpg321.h" +int icy_metaint = 0; + +#define HTTP_TIMEOUT 4 +static sigjmp_buf timeout_context; + int is_address_multicast(unsigned long address) { if ((address & 255) >= 224 && (address & 255) <= 239) @@ -165,16 +173,19 @@ * @param tcp_sock the socket use to read the stream * @param buf a buffer to receive the data * @param size size of the buffer - * @return the size of the stream read or -1 if an error occured + * @return the size of the stream read or -1 if an error occured or 0 on EOF */ static int http_read_line(int tcp_sock, char *buf, int size) { int offset = 0; + int bytes_read; do { - if (read(tcp_sock, buf + offset, 1) < 0) + if ((bytes_read = read(tcp_sock, buf + offset, 1)) < 0) return -1; + if (bytes_read == 0) + return 0; if (buf[offset] != '\r') /* Strip \r from answer */ offset++; } @@ -184,6 +195,14 @@ return offset; } +/* timeout alarm handler */ +void http_alarm (int signum) +{ + signal(SIGALRM, SIG_DFL); + siglongjmp(timeout_context, 1); +} + +/* return a socket descriptor >0 on success, else 0 on error */ int http_open(char *arg) { char *host; @@ -193,6 +212,7 @@ char http_request[PATH_MAX]; char filename[PATH_MAX]; char c; + playlist *shoutcast_pls; /* Check for URL syntax */ if (strncmp(arg, "http://", strlen("http://"))) @@ -201,9 +221,16 @@ /* Parse URL */ port = 80; host = arg + strlen("http://"); - if ((request = strchr(host, '/')) == NULL) - return (0); - *request++ = 0; + if (request = strchr(host, '/')) + { + *request++ = 0; + snprintf(filename, sizeof(filename) - strlen(host) - 75, "/%s", request); + } + else + { + sprintf(filename, "/"); + request = filename; + } if (strchr(host, ':') != NULL) /* port is specified */ { @@ -211,6 +238,13 @@ *strchr(host, ':') = 0; } + /* setup an alarm in case this socket takes too long */ + if (sigsetjmp(timeout_context, 1)) { + return 0; /* timed out */ + } + signal(SIGALRM, http_alarm); + alarm(HTTP_TIMEOUT); + /* Open a TCP socket */ if (!(tcp_sock = tcp_open(host, port))) { @@ -218,38 +252,19 @@ return (0); } - snprintf(filename, sizeof(filename) - strlen(host) - 75, "%s", request); + alarm(0); + signal(SIGALRM, SIG_DFL); /* Send HTTP GET request */ - /* Please don't use a Agent know by shoutcast (Lynx, Mozilla) seems to be reconized and print - * a html page and not the stream */ - snprintf(http_request, sizeof(http_request), "GET /%s HTTP/1.0\r\n" -/* "User-Agent: Mozilla/2.0 (Win95; I)\r\n" */ - "Pragma: no-cache\r\n" "Host: %s\r\n" "Accept: */*\r\n" "\r\n", filename, host); + /* use an Agent known by shoutcast other than (Lynx, Mozilla) + * so it sends a stream intead of an html page */ + snprintf(http_request, sizeof(http_request), "GET %s HTTP/1.0\r\n" + "User-Agent: xmms/1.2.7\r\n" "%s" + "Pragma: no-cache\r\n" "Host: %s\r\n" "Accept: */*\r\n" "\r\n", filename, (options.opt & MPG321_REMOTE_PLAY) ? "Icy-MetaData:1\r\n" : "", host); send(tcp_sock, http_request, strlen(http_request), 0); /* Parse server reply */ -#if 0 - do - read(tcp_sock, &c, sizeof(char)); - while (c != ' '); - read(tcp_sock, http_request, 4 * sizeof(char)); - http_request[4] = 0; - if (strcmp(http_request, "200 ")) - { - fprintf(stderr, "http_open: "); - do - { - read(tcp_sock, &c, sizeof(char)); - fprintf(stderr, "%c", c); - } - while (c != '\r'); - fprintf(stderr, "\n"); - return (0); - } -#endif - do { int len; @@ -272,7 +287,7 @@ return http_open(&http_request[10]); } - if (strncmp(http_request, "ICY ", 4) == 0) + else if (strncmp(http_request, "ICY ", 4) == 0) { /* This is icecast streaming */ if (strncmp(http_request + 4, "200 ", 4)) @@ -281,15 +296,54 @@ return 0; } } - else if (strncmp(http_request, "icy-", 4) == 0) + else if ((options.opt & MPG321_REMOTE_PLAY) && (strncmp(http_request, "icy-metaint:", 12) == 0)) { - /* we can have: icy-noticeX, icy-name, icy-genre, icy-url, icy-pub, icy-metaint, icy-br */ - /* Don't print these - mpg123 doesn't */ - /* fprintf(stderr,"%s\n",http_request); */ + icy_metaint = atoi(http_request + 12); + icy_buf_read = 0; + icy_tag_crossed_boundary = 0; } } while (strcmp(http_request, "\n") != 0); + /* consume HTTP body if this is a shoutcast .pls URL */ + if (strrchr(request, '.') && (strstr(strrchr(request, '.'), ".pls"))) + { + int len, i; + + if (!(shoutcast_pls = new_playlist())) + { + fprintf(stderr, "new_playlist() failed for shoutcast .pls\n"); + exit(1); + } + + do + { + len = http_read_line(tcp_sock, http_request, sizeof(http_request)); + + if (len == -1) + { + fprintf(stderr, "http_open: %s\n", strerror(errno)); + return 0; + } + + if (len && (strncmp(http_request, "File", 4) == 0)) + { + *strrchr(http_request, '\n') = '\0'; + add_file(shoutcast_pls, strchr(http_request, '=')+1); + } + } + while (len != 0); + + close(tcp_sock); + + for (i=0; i < shoutcast_pls->numfiles; i++) + { + if ((tcp_sock=http_open(shoutcast_pls->files[i])) != 0) + break; /* we have a winner */ + } + delete_playlist(shoutcast_pls); + } + return (tcp_sock); } --- mpg321-0.2.10/playlist.c 2002-03-23 23:50:12.000000000 -0600 +++ mpg321-0.2.10.dev/playlist.c 2003-11-28 00:18:53.000000000 -0600 @@ -54,7 +54,6 @@ return pl; } -/* not needed - one static playlist object per instance void delete_playlist(playlist *pl) { int i; @@ -63,7 +62,7 @@ free(pl->files); free(pl); -} */ +} void resize_playlist(playlist *pl) {