📄 shoutcast.c
字号:
/*!
* Copyright (C) 2006-2007 by egnite Software 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 EGNITE SOFTWARE GMBH 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 EGNITE
* SOFTWARE GMBH 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 receiver.
*
* \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>
#include <hxmp3/mp3dec.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <net/route.h>
#include <pro/dhcp.h>
#include "tlv320dac.h"
#include "config.h"
#include "utils.h"
#include "userif.h"
#include "mp3player.h"
#include "shoutcast.h"
#ifndef SHOUTCAST_THREAD_STACK
#ifdef AT91SAM7X_EK
#define SHOUTCAST_THREAD_STACK 1024
#else
#define SHOUTCAST_THREAD_STACK 2048
#endif
#endif
/*!
* \brief Process embedded meta data.
*/
static int ProcessMetaData(TCPSOCKET * sock, SHOUTCASTINFO * sci, u_int *status)
{
u_char blks = 0;
u_int mlen;
char *mbuf;
char *mn1;
char *mn2;
char *md1;
char *md2;
/*
* Wait for the lenght byte.
*/
if (TcpGetBuffer(sock, (char *)&blks, 1, status)) {
/* Status change or receive error. */
return -1;
}
if (blks == 0) {
/* Empty metadata block. */
return 0;
}
if (blks > 32) {
/* We are probably out of sync. */
return -1;
}
mlen = (u_int)blks * 16;
/*
* Wait for the metadata block.
*/
if ((mbuf = malloc(mlen + 1)) == 0) {
return -1;
}
mbuf[mlen] = 0;
if (TcpGetBuffer(sock, mbuf, mlen, status)) {
/* Status change or receive error. */
return -1;
}
//ClearMetaData();
printf("\nMeta=\"%s\"\n", mbuf);
mn1 = mbuf;
while (mn1) {
if ((mn2 = strchr(mn1, ';')) != 0)
*mn2++ = 0;
if ((md1 = strchr(mn1, '=')) != 0) {
*md1++ = 0;
while (*md1 == ' ' || *md1 == '\'')
md1++;
if ((md2 = strrchr(md1, '\'')) != 0)
*md2 = 0;
if (strcasecmp(mn1, "StreamTitle") == 0) {
if (sci->sci_metatitle) {
free(sci->sci_metatitle);
}
if ((sci->sci_metatitle = malloc(strlen(md1) + 1)) != NULL) {
strcpy(sci->sci_metatitle, md1);
}
} else if (strcasecmp(mn1, "StreamUrl") == 0) {
if (sci->sci_metaurl) {
free(sci->sci_metaurl);
}
if ((sci->sci_metaurl = malloc(strlen(md1) + 1)) != NULL) {
strcpy(sci->sci_metaurl, md1);
}
}
}
mn1 = mn2;
}
free(mbuf);
return 0;
}
/*!
* \brief SHOUTcast receiver thread.
*
* Reads audio data from a TCP socket and passes it to a ring buffer.
* Meta data is parsed and removed from the audio stream.
*
* \param arg Pointer to the player info structure.
*/
THREAD(ShoutCastThread, arg)
{
RECEIVERINFO *rip = (RECEIVERINFO *) arg;
SHOUTCASTINFO *sci = (SHOUTCASTINFO *) rip->ri_bcast;
STATIONINFO *sip;
int rbytes;
int got;
for (;;) {
/* Idle loop. */
for (;;) {
printf("[RIDLE]");
memset(sci, 0, sizeof(SHOUTCASTINFO));
rip->ri_status = RSTAT_IDLE;
NutEventBroadcast(&rip->ri_stsevt);
/* Wait for start event. */
NutEventWait(&rip->ri_cmdevt, 0);
printf("[REVT%u]", rip->ri_status);
if (rip->ri_status == RSTAT_START) {
break;
}
}
printf("[RRUN]");
sip = rip->ri_sip;
sci->sci_metapos = sci->sci_metaint;
/* Broadcast running event. */
rip->ri_status = RSTAT_RUNNING;
NutEventBroadcast(&rip->ri_stsevt);
/* Running loop. */
while (rip->ri_status == RSTAT_RUNNING) {
/* Wrap write pointer, if the read pointer is not at the
buffer start. */
if (rip->ri_wpos >= MP3_BUFSIZ && rip->ri_rpos > 0) {
rip->ri_wpos = 0;
}
if (rip->ri_wpos >= rip->ri_rpos) {
/* Write pointer is in front of or equal to the read pointer.
Allow to write up to the end of the buffer. */
rbytes = MP3_BUFSIZ - rip->ri_wpos;
} else {
/* Write pointer is behind read pointer. Allow to write up
to the last byte before the read pointer. */
rbytes = rip->ri_rpos - rip->ri_wpos - 1;
}
/* Honor some limit. */
if (rbytes > 4096) {
rbytes = 4096;
}
/* Do not read beyond meta data. */
if (sci->sci_metaint && rbytes > sci->sci_metapos) {
rbytes = sci->sci_metapos;
}
/* If the buffer is full, wait for a signal from the consumer. */
if (rbytes == 0) {
putchar('!');
NutEventWait(&rip->ri_wrbque, 1000);
continue;
}
if ((got = NutTcpReceive(sip->si_sock, &rip->ri_buff[rip->ri_wpos], rbytes)) < 0) {
printf("[RXFAIL(%d)%d]", rbytes, NutTcpError(sip->si_sock));
break;
}
if (got) {
rip->ri_wpos += got;
rip->ri_avail += got;
if (rip->ri_avail >= 2 * MAINBUF_SIZE) {
NutEventPost(&rip->ri_rdbque);
}
if (sci->sci_metaint) {
sci->sci_metapos -= got;
if (sci->sci_metapos == 0) {
if (ProcessMetaData(sip->si_sock, sci, &rip->ri_status)) {
break;
}
else {
UserIfShowStatus(DIST_FORCE);
}
sci->sci_metapos = sci->sci_metaint;
}
}
}
}
}
}
/*!
* \brief Create a SHOUTcast receiver instance.
*
* \param rip Pointer to the receiver information structure.
*
* \return 0 on success, -1 otherwise.
*/
int ShoutCastCreate(RECEIVERINFO * rip)
{
/* Allocate local info structure. */
if ((rip->ri_bcast = malloc(sizeof(SHOUTCASTINFO))) != NULL) {
memset(rip->ri_bcast, 0, sizeof(SHOUTCASTINFO));
/* Allocate the audio stream buffer. */
if ((rip->ri_buff = malloc(MP3_BUFSIZ + 2 * MAINBUF_SIZE)) != NULL) {
/* Start the receiver thread. */
if (NutThreadCreate("scast", ShoutCastThread, rip, SHOUTCAST_THREAD_STACK)) {
/* Success! */
return 0;
}
free(rip->ri_buff);
rip->ri_buff = NULL;
}
free(rip->ri_bcast);
rip->ri_bcast = NULL;
}
/* Not enough memory. */
return -1;
}
/*!
* \brief Setup SHOUTcast receiver.
*
* Parses the header lines of the HTTP response.
*
* \return 0 on success, or -1 if the server responded with an error code
* or doesn't seem to provide a SHOUTcast stream.
*/
int ShoutCastSetup(RECEIVERINFO * rip)
{
STATIONINFO *sip = rip->ri_sip;
SHOUTCASTINFO *sci = (SHOUTCASTINFO *) rip->ri_bcast;
int i;
char *cp;
/*
* Check if we really have a SHOUTcast server.
*/
if (strlen(sip->si_header[0]) < 7 || strncmp(sip->si_header[0], "ICY", 3)) {
return -1;
}
if (atoi(sip->si_header[0] + 4) != 200) {
return -1;
}
sip->si_stype = STATION_TYPE_SHOUTCAST;
sip->si_ptype = PLAYER_TYPE_MP3;
sci->sci_metaint = 0;
for (i = 1; sip->si_header[i]; i++) {
if ((cp = strchr(sip->si_header[i], ':')) != NULL) {
cp++;
if (strncmp(sip->si_header[i], "icy-name:", 9) == 0) {
/* Set the station name. */
sip->si_name = cp;
} else if (strncmp(sip->si_header[i], "icy-genre:", 10) == 0) {
/* Set the station's genre. */
sip->si_genre = cp;
} else if (strncmp(sip->si_header[i], "icy-metaint:", 12) == 0) {
/* Set the metadata interval. */
sci->sci_metaint = atol(cp);
} else if (strncmp(sip->si_header[i], "icy-br:", 7) == 0) {
/* Set the bit rate. */
sip->si_bitrate = atoi(cp);
} else if (strncmp(sip->si_header[i], "content-type:", 13) == 0) {
/* Check content type. Modify this to add more decoders. */
if (strcmp(cp, "audio/mpeg")) {
sip->si_ptype = 0;
}
}
}
}
return 0;
}
/*!
* \brief Receiver plug-in reference structure.
*
* Used by the application to create a SHOUTcast receiver instance.
*/
RECEIVERPLUGIN rpiShoutcast = {
ShoutCastCreate, /*!< Plugin method rp_create. */
ShoutCastSetup /*!< Plugin method rp_setup. */
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -