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

📄 shoutcast.c

📁 qt91上实现的mp3播放机,支持sam9260,参考用
💻 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 + -