⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 shoutcast.c

📁 Internet Radio Internet Radio Internet Radio Internet Radio Internet Radio
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * Copyright (C) 2006-2007 by egnite Software GmbH. All rights reserved.
 * Copyright (C) 2008 by egnite GmbH. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * For additional information see http://www.ethernut.de/
 */

/*!
 * \file shoutcast.c
 * \brief SHOUTcast communication routines.
 *
 * \verbatim
 *
 * $Log$
 *
 * \endverbatim
 */

#include <cfg/os.h>
#include <cfg/clock.h>
#include <dev/board.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#include <sys/version.h>
#include <sys/confnet.h>
#include <sys/atom.h>
#include <sys/heap.h>
#include <sys/thread.h>
#include <sys/timer.h>
#include <sys/event.h>
#include <sys/socket.h>

#if defined(USE_SOFTWARE_CODEC)
#include <hxmp3/mp3dec.h>
#endif

#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <net/route.h>
#include <pro/dhcp.h>
#include <netdb.h>

#include <pro/uxml.h>
#include <dev/vscodec.h>

#include "config.h"
#include "logmsg.h"
#include "favlist.h"
#include "utils.h"
#include "userif.h"
#include "xmlserv.h"
#include "shoutcast.h"

#ifndef SHOUTCAST_THREAD_STACK
#ifdef AT91SAM7X_EK
#define SHOUTCAST_THREAD_STACK  1024
#else
#define SHOUTCAST_THREAD_STACK  2048
#endif
#endif

#define SHOUTCAST_QLIST "www.shoutcast.com/sbin/newxml.phtml"
#define SHOUTCAST_QTUNE "www.shoutcast.com/sbin/tunein-station.pls"

static char station_genre[32];
static UXML_NODE *genre_tree;

#define ENCTYP_MPEG     1
#define ENCTYP_AAC      2
#define ENCTYP_AACP     3
#define ENCTYP_OGG      4
#define ENCTYP_WMA      5


typedef struct {
    char *s_name;   /* Station name. */
    long s_id;      /* SHOUTcast ID. */
    long s_br;      /* Bitrate. */
    int  s_enc;     /* Encoding. */
} STATION_INFO;

static int station_cnt;
static STATION_INFO *station_list;

static FILE *OpenTcpStream(TCPSOCKET *sock, CONST char *host, u_short port)
{
    u_long rx_to = MAX_TCPRCV_WAIT;

    /* Set socket options. Failures are ignored. */
    NutTcpSetSockOpt(sock, SO_RCVTIMEO, &rx_to, sizeof(rx_to));

    /* Connect the stream server. */
    LogMsg(LOG_STATION, "Connecting %s:%u\n", host, port);
    if (TcpHostConnect(sock, host, port)) {
        return NULL;
    }
    LogMsg(LOG_SHOUTCAST, "Connected\n");
    /* Associate a binary stream with the socket. */
    return _fdopen((int) ((uptr_t) sock), "r+b");
}

static int SendHttpRequest(FILE *stream, HTTP_SCHEME *schm)
{
    fputs("GET ", stream);
    if (proxy.proxy_port) {
        fprintf(stream, "http://%s", schm->schm_uri);
    }
    fprintf(stream, "/%s HTTP/1.1\r\n", schm->schm_path);
    fprintf(stream, "Host: %s\r\n", schm->schm_host);
    fputs("User-Agent: WinampMPEG/2.7\r\n" /* */
        "Icy-MetaData: 1\r\n"  /* */
        "Connection: close\r\n\r\n", stream);
    fflush(stream);

    return 0;
}

/*!
 * \brief Retrieve a genre list from the SHOUTcast server.
 */
static int ShoutCastGetGenreList(void)
{
    int rc = -1;
    TCPSOCKET *sock;
    HTTP_SCHEME *schm;
    FILE *stream;
    char *f_tags[] = { "genre", NULL };
    char *f_attribs[] = { "name", NULL };

    if ((schm = HttpSchemeParse(SHOUTCAST_QLIST)) == NULL) {
        return -1;
    }

    if ((sock = NutTcpCreateSocket()) != NULL) {
        if ((stream = OpenTcpStream(sock, schm->schm_host, schm->schm_portnum)) != NULL) {
            SendHttpRequest(stream, schm);
            if ((genre_tree = UxmlParseStream(stream, f_tags, f_attribs)) != NULL) {
                rc = 0;
            }
            fclose(stream);
        }
        NutTcpCloseSocket(sock);
    }
    HttpSchemeRelease(schm);

    return rc;
}

/*
 * List sort helper.
 */
int station_cmp(CONST void *ip1, CONST void *ip2)
{
    NutThreadYield();
    return strcasecmp(((STATION_INFO *)ip1)->s_name, ((STATION_INFO *)ip2)->s_name);
}

/*!
 * \brief Retrieve station list of a specified genre from SHOUTcast.
 */
static int ShoutCastGetStationList(CONST char *genre)
{
    int rc = -1;
    UXML_NODE *station_tree = NULL;
    TCPSOCKET *sock;
    HTTP_SCHEME *schm;
    FILE *stream;
    char *uri;
    char *f_tags[] = { "station", NULL };
    char *f_attribs[] = { "name", "mt", "id", "br", NULL };

    /* Release the current list. */
    if (station_list) {
        while (station_cnt) {
            station_cnt--;
            free(station_list[station_cnt].s_name);
        }
        free(station_list);
        station_list = NULL;
    }

    strcpy(station_genre, genre);

    /* Create the request string. */
    uri = malloc(sizeof(SHOUTCAST_QLIST) + strlen(genre) + sizeof("?genre="));
    strcpy(uri, SHOUTCAST_QLIST);
    strcat(uri, "?genre=");
    strcat(uri, genre);
    schm = HttpSchemeParse(uri);
    free(uri);
    if (schm == NULL) {
        return -1;
    }

    if ((sock = NutTcpCreateSocket()) != NULL) {
        if ((stream = OpenTcpStream(sock, schm->schm_host, schm->schm_portnum)) != NULL) {
            SendHttpRequest(stream, schm);
            if ((station_tree = UxmlParseStream(stream, f_tags, f_attribs)) != NULL) {
                rc = 0;
            }
            fclose(stream);
        }
        NutTcpCloseSocket(sock);
    }
    HttpSchemeRelease(schm);

    if (rc == 0) {
        UXML_ATTRIB *attr;
        UXML_NODE *node = station_tree;

        for (node = station_tree; node; node = node->xmln_next) {
            //Optional filter attr = node->xmln_attribs;
            station_cnt++;
        }
        LogMsg(LOG_SHOUTCAST, "\n%d Stations", station_cnt);
        if (station_cnt) {
            station_list = malloc(station_cnt * sizeof(STATION_INFO));
            memset(station_list, 0, station_cnt * sizeof(STATION_INFO));
            station_cnt = 0;
            for (node = station_tree; node; node = node->xmln_next) {
                attr = node->xmln_attribs;
                while (attr) {
                    // Repeat optional filter
                    if (strcasecmp(attr->xmla_name, "name") == 0) {
                        station_list[station_cnt].s_name = attr->xmla_value;
                        attr->xmla_value = NULL;
                    }
                    if (strcasecmp(attr->xmla_name, "id") == 0) {
                        station_list[station_cnt].s_id = atol(attr->xmla_value);
                    }
                    attr = attr->xmla_next;
                }
                if (station_list[station_cnt].s_name) {
                    station_cnt++;
                }
            }
            qsort(station_list, station_cnt, sizeof(STATION_INFO), station_cmp);
        }
        UxmlTreeDestroy(station_tree);
    }
    return rc;
}

char * ShoutCastGetGenre(int idx)
{
    UXML_NODE *node;
    UXML_ATTRIB *attr;

    if (genre_tree == NULL) {
        ShoutCastGetGenreList();
    }
    node = genre_tree;
    while (node) {
        attr = node->xmln_attribs;
        while (attr) {
            if (idx == 0) {
                return attr->xmla_value;
            }
            idx--;
            attr = attr->xmla_next;
        }
        node = node->xmln_next;
    }
    return NULL;
}

char * ShoutCastGetStationName(char *genre, int idx)
{
    if (strcmp(station_genre, genre)) {
        ShoutCastGetStationList(genre);
    }
    if (idx < station_cnt) {
        return station_list[idx].s_name;
    }
    return NULL;
}

/*!
 * \brief Query streams of a specified station and create a favorites list entry.
 *
 * \param idx SHOUTcast station list index.
 * \param pos Favorites list index.
 *
 * \return 0 on success, -1 otherwise.
 */
int ShoutCastAddStation(int idx, int pos)
{
    int rc = -1;
    TCPSOCKET *sock;
    HTTP_SCHEME *schm;
    FILE *stream;
    char *line;
    char *uri;
    int inlist;
    char *cp;

    if (idx >= station_cnt) {
        return -1;
    }
    uri = malloc(sizeof(SHOUTCAST_QTUNE) + 16);
    sprintf(uri, SHOUTCAST_QTUNE "?id=%ld", station_list[idx].s_id);
    schm = HttpSchemeParse(uri);
    free(uri);
    if (schm == NULL) {
        return -1;
    }
    if ((sock = NutTcpCreateSocket()) != NULL) {
        if ((stream = OpenTcpStream(sock, schm->schm_host, schm->schm_portnum)) != NULL) {
            SendHttpRequest(stream, schm);
            line = malloc(128);
            line[127] = 0;
            inlist = 0;
            pos = FavListSet(pos, station_list[idx].s_name, NULL);
            while (fgets(line, 127, stream)) {
                if (inlist) {
                    if ((cp = strchr(line, '\r')) == NULL) {
                        cp = strchr(line, '\n');
                    }
                    if (cp) {
                        *cp = 0;
                    }
                    cp = strchr(line, '=');
                    if (cp && strncmp(line, "File", 4) == 0) {
                        FavListSet(pos, NULL, cp + 8);
                        LogMsg(LOG_SHOUTCAST, "Added %s\n", cp + 8);
                    }
                }
                else if (strncmp(line, "[playlist]", sizeof("[playlist]") - 1) == 0) {
                    inlist = 1;
                }
            }
            free(line);
            fclose(stream);
            rc = 0;
        }
        NutTcpCloseSocket(sock);
    }
    HttpSchemeRelease(schm);

    return rc;
}

int ShoutCastGetPlayList(int id)
{
    TCPSOCKET *sock;
    u_long rx_to = MAX_TCPRCV_WAIT;
    int cr;
    int err = 0;
    char *line;
    int len;
    HTTP_SCHEME *schm;
    char *uri;
    int entries = 0;

    uri = malloc(sizeof(SHOUTCAST_QTUNE) + 16);
    sprintf(uri, SHOUTCAST_QTUNE "?id=%d", id);
    schm = HttpSchemeParse(uri);
    free(uri);
    if (schm == NULL) {
        return -1;
    }

    /* Create a TCP socket. */
    if ((sock = NutTcpCreateSocket()) == NULL) {
        HttpSchemeRelease(schm);
        return -1;
    }

    /* Set socket options. Failures are ignored. */
    NutTcpSetSockOpt(sock, SO_RCVTIMEO, &rx_to, sizeof(rx_to));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -