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

📄 shoutcast.c

📁 Internet Radio Internet Radio Internet Radio Internet Radio Internet Radio
💻 C
📖 第 1 页 / 共 2 页
字号:

    /* Connect the stream server. */
    LogMsg(LOG_STATION, "Connecting %s:%u", schm->schm_host, schm->schm_portnum);
    cr = TcpHostConnect(sock, schm->schm_host, schm->schm_portnum);
    if (cr == 0) {
        LogMsg(LOG_SHOUTCAST, "[CNCTD]");

        /*
        * Send the HTTP request.
        */
        line = malloc(256);

        if (proxy.proxy_port) {
            /* A proxy requires an absolute URI. */
            strcpy(line, "GET http://");
            strcat(line, schm->schm_uri);
        }
        else {
            strcpy(line, "GET /");
            strcat(line, schm->schm_path);
        }
        strcat(line, " HTTP/1.1\r\n");
        err = TcpPutString(sock, line);

        if (err == 0) {
            /* HTTP 1.1 requires this. So just in case... */
            sprintf(line, "Host: %s\r\n", schm->schm_host);
            err = TcpPutString(sock, line);
            LogMsg(LOG_SHOUTCAST, "%s", line);
        }

        if (err == 0) {
            /* Some SHOUTcast servers seem to require a user-agent line. 
                "Eat chalk" to get in. */
            TcpPutString(sock, "User-Agent: WinampMPEG/2.7\r\n" /* */
                "Icy-MetaData: 1\r\n"  /* */
                "Connection: close\r\n\r\n");
        }
        while (err == 0) {
            if ((len = TcpGetLine(sock, line, 255)) < 0) {
                break;
            }
            if (entries) {
                if (strncmp(line, "File", 4) == 0) {
                    LogMsg(LOG_SHOUTCAST, "OK\n");
                }
            }
            else if (strncmp(line, "numberofentries=", 16) == 0) {
                entries = atoi(line + 16);
            }
        }
        LogMsg(LOG_SHOUTCAST, "Collected %d entries\n", entries);
    }
    NutTcpCloseSocket(sock);
    HttpSchemeRelease(schm);

    return err;
}

/*!
 * \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();
    LogMsg(LOG_SHOUTCAST, "Meta=\"%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);
                }
                sci->sci_metatitle = strdup(md1);
                UserIfShowStatus(DIST_FORCE);
            } else if (strcasecmp(mn1, "StreamUrl") == 0) {
                if (sci->sci_metaurl) {
                    free(sci->sci_metaurl);
                }
                sci->sci_metaurl = strdup(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 sent;
    int got;
    char *tcpbuf;
    char *bp;
    time_t buffering;
    int timeouts;

    for (;;) {
        /* 
         * Idle loop. 
         */
        rip->ri_decoder = -1;
        for (;;) {
            memset(sci, 0, sizeof(SHOUTCASTINFO));
            rip->ri_status &= ~RSTAT_STOP;
            rip->ri_status |= RSTAT_IDLE;
            NutEventBroadcast(&rip->ri_stsevt);
            LogMsg(LOG_SHOUTCAST, "Receiver idle\n");

            /* Wait for start event. */
            NutEventWait(&rip->ri_cmdevt, 0);
            if (rip->ri_status & RSTAT_START) {
                if ((rip->ri_decoder = _open("audio0", _O_WRONLY | _O_BINARY)) != -1) {
                    rip->ri_status &= ~(RSTAT_START | RSTAT_IDLE);
                    break;
                }
                LogMsg(LOG_ERROR, "No codec device\n");
            }
        }

        sip = rip->ri_sip;
        sci->sci_metapos = sci->sci_metaint;
        /* Broadcast running event. */
        rip->ri_status |= RSTAT_BUFFERING;
        NutEventBroadcast(&rip->ri_stsevt);

        /* Set initial audio buffer watermarks and streaming start time. */
        {
            u_long sz;

            /* Get codec buffer size. */
            _ioctl(rip->ri_decoder, AUDIO_GET_PBSIZE, &sz);
            /* Use 1/3 as low watermark. */
            sz /= 3;
            _ioctl(rip->ri_decoder, AUDIO_SET_PBWLOW, &sz);
            /* Use 2/3 as high watermark. */
            sz *= 2;
            _ioctl(rip->ri_decoder, AUDIO_SET_PBWHIGH, &sz);
        }

        /* So much copying! This is the disadvantage of the independent audio driver. */
        tcpbuf = malloc(4096);

        /* Start with buffering mode. */
        time(&buffering);

        /* Clear timeout counter. */
        timeouts = 0;

        /*
         * This loop reads audio data from the SHOUTcast server and passes
         * it to the audio decoder.
         */
        while ((rip->ri_status & RSTAT_STOP) == 0) {
            rbytes = 4096;
            /* Do not read beyond meta data. */
            if (sci->sci_metaint && rbytes > sci->sci_metapos) {
                rbytes = sci->sci_metapos;
            }

            /* Receive audio data from the Internet radio server. */
            if ((got = NutTcpReceive(sip->si_sock, tcpbuf, rbytes)) < 0) {
                LogMsg(LOG_SHOUTCAST, "Receiver error %d\n", NutTcpError(sip->si_sock));
                break;
            }

            /* Stop receiver on too many timeouts. */
            if (got == 0) {
                if (timeouts++ > 10) {
                    break;
                }
                continue;
            }
            if (timeouts) {
                timeouts--;
            }

            /* Send audio data to the audio codec. */
            bp = tcpbuf;
            rbytes = got;
            while (rbytes) {
                sent = _write(rip->ri_decoder, bp, rbytes);
                if (sent < 0) {
                    rip->ri_status |= RSTAT_STOP;
                    LogMsg(LOG_WARN, "Codec write failed\n");
                    break;
                }
                rbytes -= sent;
                bp += sent;
            }

            /* Receive optional meta data. */
            if (sci->sci_metaint) {
                sci->sci_metapos -= got;
                if (sci->sci_metapos == 0) {
                    if (ProcessMetaData(sip->si_sock, sci, &rip->ri_status)) {
                        rip->ri_status |= RSTAT_STOP;
                        LogMsg(LOG_WARN, "Metadata sync lost\n");
                        break;
                    }
                    sci->sci_metapos = sci->sci_metaint;
                }
            }

            /* Force decoder start after 10 seconds. */
            if (buffering) {
                int pbstat;
                _ioctl(rip->ri_decoder, AUDIO_GET_STATUS, &pbstat);
                if (pbstat) {
                    rip->ri_status |= RSTAT_RUNNING;
                    LogMsg(LOG_SHOUTCAST, "Playback started\n");
                    UserIfShowStatus(DIST_FORCE);
                    buffering = 0;
                }
            }
            if (buffering) {
                if(time(NULL) - buffering > MAX_WAIT_MP3BUF_FILLED) {
                    rip->ri_status &= ~RSTAT_BUFFERING;
                    _ioctl(rip->ri_decoder, AUDIO_PLAY, NULL);
                    NutSleep(500);
                }
            }
        }
        free(tcpbuf);

        /* Discard any buffered audio data. */
        _ioctl(rip->ri_decoder, AUDIO_CANCEL, NULL);
        rip->ri_status &= ~(RSTAT_BUFFERING | RSTAT_RUNNING);
        _close(rip->ri_decoder);
    }
}

/*!
 * \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));

        /* Start the receiver thread. */
        if (NutThreadCreate("scast", ShoutCastThread, rip, SHOUTCAST_THREAD_STACK)) {
            /* Success! */
            return 0;
        }
        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)
{
    SHOUTCASTINFO *sci = (SHOUTCASTINFO *) rip->ri_bcast;
    int i;
    char *cp;
    int rcode;

    /* Check if we really have a SHOUTcast server. */
    if (strlen(sip->si_header[0]) > 6 && strncmp(sip->si_header[0], "ICY", 3) == 0) {
        rcode = atoi(sip->si_header[0] + 4);
        sip->si_protocol = PROTOCOL_TYPE_SHOUTCAST;
    }
    /* Alternatively check for Icecast. */
    else if (strlen(sip->si_header[0]) > 11 && strncmp(sip->si_header[0], "HTTP/1", 6) == 0) {
        rcode = atoi(sip->si_header[0] + 9);
        sip->si_protocol = PROTOCOL_TYPE_ICECAST;
    }
    /* Reject unknown server responses. */
    else {
        return -1;
    }

    if (rcode != 200) {
        return -1;
    }
    sip->si_content = CONTENT_TYPE_MP3;
    sci->sci_metaint = 0;

    for (i = 1; sip->si_header[i]; i++) {
        if ((cp = strchr(sip->si_header[i], ':')) != NULL) {
            cp++;
            while (*cp == ' ') {
                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") == 0) {
                    sip->si_content = CONTENT_TYPE_MP3;
                }
                else if (strcmp(cp, "application/ogg") == 0) {
                    sip->si_content = CONTENT_TYPE_OGG;
                }
                else if (strcmp(cp, "audio/aacp") == 0) {
                    sip->si_content = CONTENT_TYPE_AACP;
                }
                else {
                    sip->si_content = CONTENT_TYPE_UNKNOWN;
                }
            }
        }
    }
    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 + -