webradio/shoutcast.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006-2007 by egnite Software GmbH. All rights reserved.
00003  * Copyright (C) 2008 by egnite GmbH. All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. Neither the name of the copyright holders nor the names of
00015  *    contributors may be used to endorse or promote products derived
00016  *    from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00019  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00021  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00022  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00024  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00025  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00026  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00027  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00028  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00029  * SUCH DAMAGE.
00030  *
00031  * For additional information see http://www.ethernut.de/
00032  */
00033 
00045 #include <cfg/os.h>
00046 #include <cfg/clock.h>
00047 #include <dev/board.h>
00048 
00049 #include <stdlib.h>
00050 #include <string.h>
00051 #include <stdio.h>
00052 #include <fcntl.h>
00053 #include <io.h>
00054 
00055 #include <sys/version.h>
00056 #include <sys/confnet.h>
00057 #include <sys/atom.h>
00058 #include <sys/heap.h>
00059 #include <sys/thread.h>
00060 #include <sys/timer.h>
00061 #include <sys/event.h>
00062 #include <sys/socket.h>
00063 
00064 #if defined(USE_SOFTWARE_CODEC)
00065 #include <hxmp3/mp3dec.h>
00066 #endif
00067 
00068 #include <netinet/tcp.h>
00069 #include <arpa/inet.h>
00070 #include <net/route.h>
00071 #include <pro/dhcp.h>
00072 #include <netdb.h>
00073 
00074 #include <pro/uxml.h>
00075 #include <dev/vscodec.h>
00076 
00077 #include "config.h"
00078 #include "logmsg.h"
00079 #include "favlist.h"
00080 #include "utils.h"
00081 #include "userif.h"
00082 #include "xmlserv.h"
00083 #include "shoutcast.h"
00084 
00085 #ifndef SHOUTCAST_THREAD_STACK
00086 #ifdef AT91SAM7X_EK
00087 #define SHOUTCAST_THREAD_STACK  1024
00088 #else
00089 #define SHOUTCAST_THREAD_STACK  2048
00090 #endif
00091 #endif
00092 
00093 #define SHOUTCAST_QLIST "www.shoutcast.com/sbin/newxml.phtml"
00094 #define SHOUTCAST_QTUNE "www.shoutcast.com/sbin/tunein-station.pls"
00095 
00096 static char station_genre[32];
00097 static UXML_NODE *genre_tree;
00098 
00099 #define ENCTYP_MPEG     1
00100 #define ENCTYP_AAC      2
00101 #define ENCTYP_AACP     3
00102 #define ENCTYP_OGG      4
00103 #define ENCTYP_WMA      5
00104 
00105 
00106 typedef struct {
00107     char *s_name;   /* Station name. */
00108     long s_id;      /* SHOUTcast ID. */
00109     long s_br;      /* Bitrate. */
00110     int  s_enc;     /* Encoding. */
00111 } STATION_INFO;
00112 
00113 static int station_cnt;
00114 static STATION_INFO *station_list;
00115 
00116 static FILE *OpenTcpStream(TCPSOCKET *sock, CONST char *host, u_short port)
00117 {
00118     u_long rx_to = MAX_TCPRCV_WAIT;
00119 
00120     /* Set socket options. Failures are ignored. */
00121     NutTcpSetSockOpt(sock, SO_RCVTIMEO, &rx_to, sizeof(rx_to));
00122 
00123     /* Connect the stream server. */
00124     LogMsg(LOG_STATION, "Connecting %s:%u\n", host, port);
00125     if (TcpHostConnect(sock, host, port)) {
00126         return NULL;
00127     }
00128     LogMsg(LOG_SHOUTCAST, "Connected\n");
00129     /* Associate a binary stream with the socket. */
00130     return _fdopen((int) ((uptr_t) sock), "r+b");
00131 }
00132 
00133 static int SendHttpRequest(FILE *stream, HTTP_SCHEME *schm)
00134 {
00135     fputs("GET ", stream);
00136     if (proxy.proxy_port) {
00137         fprintf(stream, "http://%s", schm->schm_uri);
00138     }
00139     fprintf(stream, "/%s HTTP/1.1\r\n", schm->schm_path);
00140     fprintf(stream, "Host: %s\r\n", schm->schm_host);
00141     fputs("User-Agent: WinampMPEG/2.7\r\n" /* */
00142         "Icy-MetaData: 1\r\n"  /* */
00143         "Connection: close\r\n\r\n", stream);
00144     fflush(stream);
00145 
00146     return 0;
00147 }
00148 
00152 static int ShoutCastGetGenreList(void)
00153 {
00154     int rc = -1;
00155     TCPSOCKET *sock;
00156     HTTP_SCHEME *schm;
00157     FILE *stream;
00158     char *f_tags[] = { "genre", NULL };
00159     char *f_attribs[] = { "name", NULL };
00160 
00161     if ((schm = HttpSchemeParse(SHOUTCAST_QLIST)) == NULL) {
00162         return -1;
00163     }
00164 
00165     if ((sock = NutTcpCreateSocket()) != NULL) {
00166         if ((stream = OpenTcpStream(sock, schm->schm_host, schm->schm_portnum)) != NULL) {
00167             SendHttpRequest(stream, schm);
00168             if ((genre_tree = UxmlParseStream(stream, f_tags, f_attribs)) != NULL) {
00169                 rc = 0;
00170             }
00171             fclose(stream);
00172         }
00173         NutTcpCloseSocket(sock);
00174     }
00175     HttpSchemeRelease(schm);
00176 
00177     return rc;
00178 }
00179 
00180 /*
00181  * List sort helper.
00182  */
00183 int station_cmp(CONST void *ip1, CONST void *ip2)
00184 {
00185     NutThreadYield();
00186     return strcasecmp(((STATION_INFO *)ip1)->s_name, ((STATION_INFO *)ip2)->s_name);
00187 }
00188 
00192 static int ShoutCastGetStationList(CONST char *genre)
00193 {
00194     int rc = -1;
00195     UXML_NODE *station_tree = NULL;
00196     TCPSOCKET *sock;
00197     HTTP_SCHEME *schm;
00198     FILE *stream;
00199     char *uri;
00200     char *f_tags[] = { "station", NULL };
00201     char *f_attribs[] = { "name", "mt", "id", "br", NULL };
00202 
00203     /* Release the current list. */
00204     if (station_list) {
00205         while (station_cnt) {
00206             station_cnt--;
00207             free(station_list[station_cnt].s_name);
00208         }
00209         free(station_list);
00210         station_list = NULL;
00211     }
00212 
00213     strcpy(station_genre, genre);
00214 
00215     /* Create the request string. */
00216     uri = malloc(sizeof(SHOUTCAST_QLIST) + strlen(genre) + sizeof("?genre="));
00217     strcpy(uri, SHOUTCAST_QLIST);
00218     strcat(uri, "?genre=");
00219     strcat(uri, genre);
00220     schm = HttpSchemeParse(uri);
00221     free(uri);
00222     if (schm == NULL) {
00223         return -1;
00224     }
00225 
00226     if ((sock = NutTcpCreateSocket()) != NULL) {
00227         if ((stream = OpenTcpStream(sock, schm->schm_host, schm->schm_portnum)) != NULL) {
00228             SendHttpRequest(stream, schm);
00229             if ((station_tree = UxmlParseStream(stream, f_tags, f_attribs)) != NULL) {
00230                 rc = 0;
00231             }
00232             fclose(stream);
00233         }
00234         NutTcpCloseSocket(sock);
00235     }
00236     HttpSchemeRelease(schm);
00237 
00238     if (rc == 0) {
00239         UXML_ATTRIB *attr;
00240         UXML_NODE *node = station_tree;
00241 
00242         for (node = station_tree; node; node = node->xmln_next) {
00243             //Optional filter attr = node->xmln_attribs;
00244             station_cnt++;
00245         }
00246         LogMsg(LOG_SHOUTCAST, "\n%d Stations", station_cnt);
00247         if (station_cnt) {
00248             station_list = malloc(station_cnt * sizeof(STATION_INFO));
00249             memset(station_list, 0, station_cnt * sizeof(STATION_INFO));
00250             station_cnt = 0;
00251             for (node = station_tree; node; node = node->xmln_next) {
00252                 attr = node->xmln_attribs;
00253                 while (attr) {
00254                     // Repeat optional filter
00255                     if (strcasecmp(attr->xmla_name, "name") == 0) {
00256                         station_list[station_cnt].s_name = attr->xmla_value;
00257                         attr->xmla_value = NULL;
00258                     }
00259                     if (strcasecmp(attr->xmla_name, "id") == 0) {
00260                         station_list[station_cnt].s_id = atol(attr->xmla_value);
00261                     }
00262                     attr = attr->xmla_next;
00263                 }
00264                 if (station_list[station_cnt].s_name) {
00265                     station_cnt++;
00266                 }
00267             }
00268             qsort(station_list, station_cnt, sizeof(STATION_INFO), station_cmp);
00269         }
00270         UxmlTreeDestroy(station_tree);
00271     }
00272     return rc;
00273 }
00274 
00275 char * ShoutCastGetGenre(int idx)
00276 {
00277     UXML_NODE *node;
00278     UXML_ATTRIB *attr;
00279 
00280     if (genre_tree == NULL) {
00281         ShoutCastGetGenreList();
00282     }
00283     node = genre_tree;
00284     while (node) {
00285         attr = node->xmln_attribs;
00286         while (attr) {
00287             if (idx == 0) {
00288                 return attr->xmla_value;
00289             }
00290             idx--;
00291             attr = attr->xmla_next;
00292         }
00293         node = node->xmln_next;
00294     }
00295     return NULL;
00296 }
00297 
00298 char * ShoutCastGetStationName(char *genre, int idx)
00299 {
00300     if (strcmp(station_genre, genre)) {
00301         ShoutCastGetStationList(genre);
00302     }
00303     if (idx < station_cnt) {
00304         return station_list[idx].s_name;
00305     }
00306     return NULL;
00307 }
00308 
00317 int ShoutCastAddStation(int idx, int pos)
00318 {
00319     int rc = -1;
00320     TCPSOCKET *sock;
00321     HTTP_SCHEME *schm;
00322     FILE *stream;
00323     char *line;
00324     char *uri;
00325     int inlist;
00326     char *cp;
00327 
00328     if (idx >= station_cnt) {
00329         return -1;
00330     }
00331     uri = malloc(sizeof(SHOUTCAST_QTUNE) + 16);
00332     sprintf(uri, SHOUTCAST_QTUNE "?id=%ld", station_list[idx].s_id);
00333     schm = HttpSchemeParse(uri);
00334     free(uri);
00335     if (schm == NULL) {
00336         return -1;
00337     }
00338     if ((sock = NutTcpCreateSocket()) != NULL) {
00339         if ((stream = OpenTcpStream(sock, schm->schm_host, schm->schm_portnum)) != NULL) {
00340             SendHttpRequest(stream, schm);
00341             line = malloc(128);
00342             line[127] = 0;
00343             inlist = 0;
00344             pos = FavListSet(pos, station_list[idx].s_name, NULL);
00345             while (fgets(line, 127, stream)) {
00346                 if (inlist) {
00347                     if ((cp = strchr(line, '\r')) == NULL) {
00348                         cp = strchr(line, '\n');
00349                     }
00350                     if (cp) {
00351                         *cp = 0;
00352                     }
00353                     cp = strchr(line, '=');
00354                     if (cp && strncmp(line, "File", 4) == 0) {
00355                         FavListSet(pos, NULL, cp + 8);
00356                         LogMsg(LOG_SHOUTCAST, "Added %s\n", cp + 8);
00357                     }
00358                 }
00359                 else if (strncmp(line, "[playlist]", sizeof("[playlist]") - 1) == 0) {
00360                     inlist = 1;
00361                 }
00362             }
00363             free(line);
00364             fclose(stream);
00365             rc = 0;
00366         }
00367         NutTcpCloseSocket(sock);
00368     }
00369     HttpSchemeRelease(schm);
00370 
00371     return rc;
00372 }
00373 
00374 int ShoutCastGetPlayList(int id)
00375 {
00376     TCPSOCKET *sock;
00377     u_long rx_to = MAX_TCPRCV_WAIT;
00378     int cr;
00379     int err = 0;
00380     char *line;
00381     int len;
00382     HTTP_SCHEME *schm;
00383     char *uri;
00384     int entries = 0;
00385 
00386     uri = malloc(sizeof(SHOUTCAST_QTUNE) + 16);
00387     sprintf(uri, SHOUTCAST_QTUNE "?id=%d", id);
00388     schm = HttpSchemeParse(uri);
00389     free(uri);
00390     if (schm == NULL) {
00391         return -1;
00392     }
00393 
00394     /* Create a TCP socket. */
00395     if ((sock = NutTcpCreateSocket()) == NULL) {
00396         HttpSchemeRelease(schm);
00397         return -1;
00398     }
00399 
00400     /* Set socket options. Failures are ignored. */
00401     NutTcpSetSockOpt(sock, SO_RCVTIMEO, &rx_to, sizeof(rx_to));
00402 
00403     /* Connect the stream server. */
00404     LogMsg(LOG_STATION, "Connecting %s:%u", schm->schm_host, schm->schm_portnum);
00405     cr = TcpHostConnect(sock, schm->schm_host, schm->schm_portnum);
00406     if (cr == 0) {
00407         LogMsg(LOG_SHOUTCAST, "[CNCTD]");
00408 
00409         /*
00410         * Send the HTTP request.
00411         */
00412         line = malloc(256);
00413 
00414         if (proxy.proxy_port) {
00415             /* A proxy requires an absolute URI. */
00416             strcpy(line, "GET http://");
00417             strcat(line, schm->schm_uri);
00418         }
00419         else {
00420             strcpy(line, "GET /");
00421             strcat(line, schm->schm_path);
00422         }
00423         strcat(line, " HTTP/1.1\r\n");
00424         err = TcpPutString(sock, line);
00425 
00426         if (err == 0) {
00427             /* HTTP 1.1 requires this. So just in case... */
00428             sprintf(line, "Host: %s\r\n", schm->schm_host);
00429             err = TcpPutString(sock, line);
00430             LogMsg(LOG_SHOUTCAST, "%s", line);
00431         }
00432 
00433         if (err == 0) {
00434             /* Some SHOUTcast servers seem to require a user-agent line. 
00435                 "Eat chalk" to get in. */
00436             TcpPutString(sock, "User-Agent: WinampMPEG/2.7\r\n" /* */
00437                 "Icy-MetaData: 1\r\n"  /* */
00438                 "Connection: close\r\n\r\n");
00439         }
00440         while (err == 0) {
00441             if ((len = TcpGetLine(sock, line, 255)) < 0) {
00442                 break;
00443             }
00444             if (entries) {
00445                 if (strncmp(line, "File", 4) == 0) {
00446                     LogMsg(LOG_SHOUTCAST, "OK\n");
00447                 }
00448             }
00449             else if (strncmp(line, "numberofentries=", 16) == 0) {
00450                 entries = atoi(line + 16);
00451             }
00452         }
00453         LogMsg(LOG_SHOUTCAST, "Collected %d entries\n", entries);
00454     }
00455     NutTcpCloseSocket(sock);
00456     HttpSchemeRelease(schm);
00457 
00458     return err;
00459 }
00460 
00464 static int ProcessMetaData(TCPSOCKET * sock, SHOUTCASTINFO * sci, u_int *status)
00465 {
00466     u_char blks = 0;
00467     u_int mlen;
00468     char *mbuf;
00469     char *mn1;
00470     char *mn2;
00471     char *md1;
00472     char *md2;
00473 
00474     /*
00475      * Wait for the lenght byte.
00476      */
00477     if (TcpGetBuffer(sock, (char *)&blks, 1, status)) {
00478         /* Status change or receive error. */
00479         return -1;
00480     }
00481 
00482     if (blks == 0) {
00483         /* Empty metadata block. */
00484         return 0;
00485     }
00486     if (blks > 32) {
00487         /* We are probably out of sync. */
00488         return -1;
00489     }
00490     mlen = (u_int)blks * 16;
00491 
00492     /*
00493      * Wait for the metadata block.
00494      */
00495     if ((mbuf = malloc(mlen + 1)) == 0) {
00496         return -1;
00497     }
00498     mbuf[mlen] = 0;
00499     if (TcpGetBuffer(sock, mbuf, mlen, status)) {
00500         /* Status change or receive error. */
00501         return -1;
00502     }
00503 
00504     //ClearMetaData();
00505     LogMsg(LOG_SHOUTCAST, "Meta=\"%s\"\n", mbuf);
00506     mn1 = mbuf;
00507     while (mn1) {
00508         if ((mn2 = strchr(mn1, ';')) != 0)
00509             *mn2++ = 0;
00510         if ((md1 = strchr(mn1, '=')) != 0) {
00511             *md1++ = 0;
00512             while (*md1 == ' ' || *md1 == '\'')
00513                 md1++;
00514             if ((md2 = strrchr(md1, '\'')) != 0)
00515                 *md2 = 0;
00516             if (strcasecmp(mn1, "StreamTitle") == 0) {
00517                 if (sci->sci_metatitle) {
00518                     free(sci->sci_metatitle);
00519                 }
00520                 sci->sci_metatitle = strdup(md1);
00521                 UserIfShowStatus(DIST_FORCE);
00522             } else if (strcasecmp(mn1, "StreamUrl") == 0) {
00523                 if (sci->sci_metaurl) {
00524                     free(sci->sci_metaurl);
00525                 }
00526                 sci->sci_metaurl = strdup(md1);
00527             }
00528         }
00529         mn1 = mn2;
00530     }
00531     free(mbuf);
00532 
00533     return 0;
00534 }
00535 
00544 THREAD(ShoutCastThread, arg)
00545 {
00546     RECEIVERINFO *rip = (RECEIVERINFO *) arg;
00547     SHOUTCASTINFO *sci = (SHOUTCASTINFO *) rip->ri_bcast;
00548     STATIONINFO *sip;
00549     int rbytes;
00550     int sent;
00551     int got;
00552     char *tcpbuf;
00553     char *bp;
00554     time_t buffering;
00555     int timeouts;
00556 
00557     for (;;) {
00558         /* 
00559          * Idle loop. 
00560          */
00561         rip->ri_decoder = -1;
00562         for (;;) {
00563             memset(sci, 0, sizeof(SHOUTCASTINFO));
00564             rip->ri_status &= ~RSTAT_STOP;
00565             rip->ri_status |= RSTAT_IDLE;
00566             NutEventBroadcast(&rip->ri_stsevt);
00567             LogMsg(LOG_SHOUTCAST, "Receiver idle\n");
00568 
00569             /* Wait for start event. */
00570             NutEventWait(&rip->ri_cmdevt, 0);
00571             if (rip->ri_status & RSTAT_START) {
00572                 if ((rip->ri_decoder = _open("audio0", _O_WRONLY | _O_BINARY)) != -1) {
00573                     rip->ri_status &= ~(RSTAT_START | RSTAT_IDLE);
00574                     break;
00575                 }
00576                 LogMsg(LOG_ERROR, "No codec device\n");
00577             }
00578         }
00579 
00580         sip = rip->ri_sip;
00581         sci->sci_metapos = sci->sci_metaint;
00582         /* Broadcast running event. */
00583         rip->ri_status |= RSTAT_BUFFERING;
00584         NutEventBroadcast(&rip->ri_stsevt);
00585 
00586         /* Set initial audio buffer watermarks and streaming start time. */
00587         {
00588             u_long sz;
00589 
00590             /* Get codec buffer size. */
00591             _ioctl(rip->ri_decoder, AUDIO_GET_PBSIZE, &sz);
00592             /* Use 1/3 as low watermark. */
00593             sz /= 3;
00594             _ioctl(rip->ri_decoder, AUDIO_SET_PBWLOW, &sz);
00595             /* Use 2/3 as high watermark. */
00596             sz *= 2;
00597             _ioctl(rip->ri_decoder, AUDIO_SET_PBWHIGH, &sz);
00598         }
00599 
00600         /* So much copying! This is the disadvantage of the independent audio driver. */
00601         tcpbuf = malloc(4096);
00602 
00603         /* Start with buffering mode. */
00604         time(&buffering);
00605 
00606         /* Clear timeout counter. */
00607         timeouts = 0;
00608 
00609         /*
00610          * This loop reads audio data from the SHOUTcast server and passes
00611          * it to the audio decoder.
00612          */
00613         while ((rip->ri_status & RSTAT_STOP) == 0) {
00614             rbytes = 4096;
00615             /* Do not read beyond meta data. */
00616             if (sci->sci_metaint && rbytes > sci->sci_metapos) {
00617                 rbytes = sci->sci_metapos;
00618             }
00619 
00620             /* Receive audio data from the Internet radio server. */
00621             if ((got = NutTcpReceive(sip->si_sock, tcpbuf, rbytes)) < 0) {
00622                 LogMsg(LOG_SHOUTCAST, "Receiver error %d\n", NutTcpError(sip->si_sock));
00623                 break;
00624             }
00625 
00626             /* Stop receiver on too many timeouts. */
00627             if (got == 0) {
00628                 if (timeouts++ > 10) {
00629                     break;
00630                 }
00631                 continue;
00632             }
00633             if (timeouts) {
00634                 timeouts--;
00635             }
00636 
00637             /* Send audio data to the audio codec. */
00638             bp = tcpbuf;
00639             rbytes = got;
00640             while (rbytes) {
00641                 sent = _write(rip->ri_decoder, bp, rbytes);
00642                 if (sent < 0) {
00643                     rip->ri_status |= RSTAT_STOP;
00644                     LogMsg(LOG_WARN, "Codec write failed\n");
00645                     break;
00646                 }
00647                 rbytes -= sent;
00648                 bp += sent;
00649             }
00650 
00651             /* Receive optional meta data. */
00652             if (sci->sci_metaint) {
00653                 sci->sci_metapos -= got;
00654                 if (sci->sci_metapos == 0) {
00655                     if (ProcessMetaData(sip->si_sock, sci, &rip->ri_status)) {
00656                         rip->ri_status |= RSTAT_STOP;
00657                         LogMsg(LOG_WARN, "Metadata sync lost\n");
00658                         break;
00659                     }
00660                     sci->sci_metapos = sci->sci_metaint;
00661                 }
00662             }
00663 
00664             /* Force decoder start after 10 seconds. */
00665             if (buffering) {
00666                 int pbstat;
00667                 _ioctl(rip->ri_decoder, AUDIO_GET_STATUS, &pbstat);
00668                 if (pbstat) {
00669                     rip->ri_status |= RSTAT_RUNNING;
00670                     LogMsg(LOG_SHOUTCAST, "Playback started\n");
00671                     UserIfShowStatus(DIST_FORCE);
00672                     buffering = 0;
00673                 }
00674             }
00675             if (buffering) {
00676                 if(time(NULL) - buffering > MAX_WAIT_MP3BUF_FILLED) {
00677                     rip->ri_status &= ~RSTAT_BUFFERING;
00678                     _ioctl(rip->ri_decoder, AUDIO_PLAY, NULL);
00679                     NutSleep(500);
00680                 }
00681             }
00682         }
00683         free(tcpbuf);
00684 
00685         /* Discard any buffered audio data. */
00686         _ioctl(rip->ri_decoder, AUDIO_CANCEL, NULL);
00687         rip->ri_status &= ~(RSTAT_BUFFERING | RSTAT_RUNNING);
00688         _close(rip->ri_decoder);
00689     }
00690 }
00691 
00699 int ShoutCastCreate(RECEIVERINFO * rip)
00700 {
00701     /* Allocate local info structure. */
00702     if ((rip->ri_bcast = malloc(sizeof(SHOUTCASTINFO))) != NULL) {
00703         memset(rip->ri_bcast, 0, sizeof(SHOUTCASTINFO));
00704 
00705         /* Start the receiver thread. */
00706         if (NutThreadCreate("scast", ShoutCastThread, rip, SHOUTCAST_THREAD_STACK)) {
00707             /* Success! */
00708             return 0;
00709         }
00710         free(rip->ri_bcast);
00711         rip->ri_bcast = NULL;
00712     }
00713     /* Not enough memory. */
00714     return -1;
00715 }
00716 
00725 int ShoutCastSetup(RECEIVERINFO * rip, STATIONINFO *sip)
00726 {
00727     SHOUTCASTINFO *sci = (SHOUTCASTINFO *) rip->ri_bcast;
00728     int i;
00729     char *cp;
00730     int rcode;
00731 
00732     /* Check if we really have a SHOUTcast server. */
00733     if (strlen(sip->si_header[0]) > 6 && strncmp(sip->si_header[0], "ICY", 3) == 0) {
00734         rcode = atoi(sip->si_header[0] + 4);
00735         sip->si_protocol = PROTOCOL_TYPE_SHOUTCAST;
00736     }
00737     /* Alternatively check for Icecast. */
00738     else if (strlen(sip->si_header[0]) > 11 && strncmp(sip->si_header[0], "HTTP/1", 6) == 0) {
00739         rcode = atoi(sip->si_header[0] + 9);
00740         sip->si_protocol = PROTOCOL_TYPE_ICECAST;
00741     }
00742     /* Reject unknown server responses. */
00743     else {
00744         return -1;
00745     }
00746 
00747     if (rcode != 200) {
00748         return -1;
00749     }
00750     sip->si_content = CONTENT_TYPE_MP3;
00751     sci->sci_metaint = 0;
00752 
00753     for (i = 1; sip->si_header[i]; i++) {
00754         if ((cp = strchr(sip->si_header[i], ':')) != NULL) {
00755             cp++;
00756             while (*cp == ' ') {
00757                 cp++;
00758             }
00759             if (strncmp(sip->si_header[i], "icy-name:", 9) == 0) {
00760                 /* Set the station name. */
00761                 sip->si_name = cp;
00762             } else if (strncmp(sip->si_header[i], "icy-genre:", 10) == 0) {
00763                 /* Set the station's genre. */
00764                 sip->si_genre = cp;
00765             } else if (strncmp(sip->si_header[i], "icy-metaint:", 12) == 0) {
00766                 /* Set the metadata interval. */
00767                 sci->sci_metaint = atol(cp);
00768             } else if (strncmp(sip->si_header[i], "icy-br:", 7) == 0) {
00769                 /* Set the bit rate. */
00770                 sip->si_bitrate = atoi(cp);
00771             } else if (strncmp(sip->si_header[i], "content-type:", 13) == 0) {
00772                 /* Check content type. Modify this to add more decoders. */
00773                 if (strcmp(cp, "audio/mpeg") == 0) {
00774                     sip->si_content = CONTENT_TYPE_MP3;
00775                 }
00776                 else if (strcmp(cp, "application/ogg") == 0) {
00777                     sip->si_content = CONTENT_TYPE_OGG;
00778                 }
00779                 else if (strcmp(cp, "audio/aacp") == 0) {
00780                     sip->si_content = CONTENT_TYPE_AACP;
00781                 }
00782                 else {
00783                     sip->si_content = CONTENT_TYPE_UNKNOWN;
00784                 }
00785             }
00786         }
00787     }
00788     return 0;
00789 }
00790 
00796 RECEIVERPLUGIN rpiShoutcast = {
00797     ShoutCastCreate,    
00798     ShoutCastSetup      
00799 };
00800 

© 2008 by egnite GmbH - visit www.ethernut.de