| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- /*
- * Copyright (C) 2003-2006 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 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/
- */
- /*!
- * $Id: player.c 4115 2012-04-12 21:06:13Z olereinhardt $
- */
- #include <sys/heap.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <sys/thread.h>
- #include <dev/vs1001k.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include "player.h"
- #include <sys/bankmem.h>
- #define MAX_WAITSTREAM 20
- #define BIGBUF_WATERMARK 65535UL
- PLAYERINFO player;
- #if defined(__AVR__)
- /*
- * Process embedded meta data.
- */
- static void ClearMetaData(void)
- {
- if (player.psi_metatitle) {
- free(player.psi_metatitle);
- player.psi_metatitle = 0;
- }
- if (player.psi_metaurl) {
- free(player.psi_metaurl);
- player.psi_metaurl = 0;
- }
- }
- /*
- * Process embedded meta data.
- */
- static int ProcessMetaData(void)
- {
- uint8_t blks = 0;
- uint16_t cnt;
- int got;
- int rc = 0;
- uint8_t to_cnt = 0;
- char *mbuf;
- char *mn1;
- char *mn2;
- char *md1;
- char *md2;
- /*
- * Wait for the lenght byte.
- */
- while (player.psi_status == PSI_RUNNING) {
- if ((got = NutTcpReceive(player.psi_sock, &blks, 1)) == 1)
- break;
- if (got < 0 || to_cnt++ > MAX_WAITSTREAM) {
- printf("[NoLen]");
- return -1;
- }
- }
- if (blks) {
- if (blks > 32) {
- printf("[Blks=%u]", blks);
- return -1;
- }
- cnt = blks * 16;
- if ((mbuf = malloc(cnt + 1)) == 0) {
- printf("[NoMem]");
- return -1;
- }
- /*
- * Receive the metadata block.
- */
- while (player.psi_status == PSI_RUNNING) {
- if ((got = NutTcpReceive(player.psi_sock, mbuf + rc, cnt)) < 0) {
- printf("[RxFail]");
- return -1;
- }
- if (got) {
- to_cnt = 0;
- if ((cnt -= got) == 0)
- break;
- rc += got;
- mbuf[rc] = 0;
- } else if (to_cnt++ > MAX_WAITSTREAM) {
- printf("[RxTo]");
- 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 && player.psi_metatitle == 0) {
- player.psi_metatitle = malloc(strlen(md1) + 1);
- strcpy(player.psi_metatitle, md1);
- } else if (strcasecmp(mn1, "StreamUrl") == 0 && player.psi_metaurl == 0) {
- player.psi_metaurl = malloc(strlen(md1) + 1);
- strcpy(player.psi_metaurl, md1);
- }
- }
- mn1 = mn2;
- }
- free(mbuf);
- player.psi_metaupdate = 1;
- }
- return 0;
- }
- /*
- * Background thread for playing stream.
- */
- THREAD(Player, arg)
- {
- size_t rbytes;
- char *mp3buf;
- uint8_t to_cnt = 0;
- int got;
- uint8_t ief;
- /*
- * Nut/OS threads run forever.
- */
- for (;;) {
- /*
- * Idle loop.
- */
- for (;;) {
- /* Broadcast idle status. */
- printf("[IDLE]");
- ClearMetaData();
- player.psi_status = PSI_IDLE;
- NutEventBroadcast(&player.psi_stsevt);
- /* Wait for start event. */
- NutEventWait(&player.psi_cmdevt, 0);
- printf("[EVT%u]", player.psi_status);
- if (player.psi_status == PSI_START)
- break;
- }
- /* Broadcast running event. */
- printf("[RUN]");
- player.psi_status = PSI_RUNNING;
- player.psi_mp3left = player.psi_metaint;
- NutEventBroadcast(&player.psi_stsevt);
- /* Reset decoder buffer. */
- ief = VsPlayerInterrupts(0);
- NutSegBufReset();
- VsPlayerInterrupts(ief);
- /*
- * Running loop.
- */
- while (player.psi_status == PSI_RUNNING) {
- /*
- * Query number of byte available in MP3 buffer. If it is
- * zero, then the player may have stopped running.
- */
- ief = VsPlayerInterrupts(0);
- mp3buf = NutSegBufWriteRequest(&rbytes);
- VsPlayerInterrupts(ief);
- if (rbytes < 1024) {
- if (VsGetStatus() != VS_STATUS_RUNNING)
- VsPlayerKick();
- if (rbytes == 0) {
- NutSleep(125);
- continue;
- }
- }
- /* Do not read pass metadata. */
- if (player.psi_metaint && rbytes > player.psi_mp3left)
- rbytes = player.psi_mp3left;
- /*
- * Loop until MP3 buffer space is filled.
- */
- while (rbytes) {
- /* Read data directly into the MP3 buffer. */
- if ((got = NutTcpReceive(player.psi_sock, mp3buf, rbytes)) < 0) {
- /* This is fatal. Return to idle state. */
- printf("[RXFAIL]");
- player.psi_status = PSI_IDLE;
- break;
- }
- /*
- * Got some MP3 data.
- */
- if (got) {
- /* Commit the buffer. */
- ief = VsPlayerInterrupts(0);
- mp3buf = NutSegBufWriteCommit(got);
- VsPlayerInterrupts(ief);
- /* Reset timeout counter and reduce number of buffer bytes. */
- to_cnt = 0;
- rbytes -= got;
- /* Process meta data. */
- if (player.psi_metaint) {
- player.psi_mp3left -= got;
- if (player.psi_mp3left == 0) {
- if (ProcessMetaData()) {
- printf("[METAFAIL]");
- //player.psi_status = PSI_IDLE;
- //break;
- }
- player.psi_mp3left = player.psi_metaint;
- }
- }
- /* Start early with large buffers. */
- if (VsGetStatus() != VS_STATUS_RUNNING) {
- ief = VsPlayerInterrupts(0);
- if ((NutSegBufUsed() > 2048 && player.psi_start) ||
- (NutSegBufUsed() > BIGBUF_WATERMARK && NutSegBufAvailable() < BIGBUF_WATERMARK))
- VsPlayerKick();
- else
- VsPlayerInterrupts(ief);
- }
- player.psi_start = 0;
- }
- /*
- * Got no MP3 data.
- */
- else {
- printf("[T%u, %u]", to_cnt, NutHeapAvailable());
- NutSleep(100);
- if (to_cnt++ > MAX_WAITSTREAM) {
- /* If timeouts reach our limit, go back to idle state. */
- printf("[RXTO]");
- player.psi_status = PSI_IDLE;
- break;
- }
- }
- /*
- * If we won't yield the CPU, we may consume the complete
- * TCP buffer before being stopped in NutTcpReceive. That
- * would result in bumpy feeding.
- */
- NutThreadYield();
- }
- /* If play loop is not entered, we may omit the watchdog
- update here and force a system reset. */
- NutThreadYield();
- }
- /* Flush decoder and wait until finished. */
- printf("[FLUSH]");
- VsPlayerFlush();
- while (VsGetStatus() == VS_STATUS_RUNNING) {
- NutSleep(63);
- }
- /* Reset the decoder. */
- printf("[RESET]");
- VsPlayerReset(0);
- }
- }
- /*!
- * \brief Initialize the MP3 player.
- *
- * Initializes the decoder and the decoder buffer and starts the
- * player background thread.
- */
- int PlayerInit(void)
- {
- /* Init decoder. */
- if (VsPlayerInit() || VsPlayerReset(0))
- return -1;
- /* Start player thread. */
- if (NutThreadCreate("player", Player, 0, 512) == 0)
- return -1;
- return 0;
- }
- /*!
- * \brief Stop MP3 player.
- *
- * Force player into idle mode.
- */
- int PlayerStop(uint32_t tmo)
- {
- while (player.psi_status != PSI_IDLE) {
- printf("[STOP]");
- player.psi_status = PSI_STOP;
- NutEventPost(&player.psi_cmdevt);
- if (NutEventWait(&player.psi_stsevt, tmo)) {
- printf("[TOP]");
- return -1;
- }
- }
- printf("[STOPPED]");
- return 0;
- }
- /*!
- * \brief Start MP3 player.
- *
- * If the player is currently running, it will be stopped first.
- */
- int PlayerStart(TCPSOCKET * sock, uint32_t metaint, uint32_t tmo)
- {
- if (PlayerStop(tmo))
- return -1;
- /* Clear event queue and send start command. */
- NutEventBroadcast(&player.psi_stsevt);
- printf("[START]");
- player.psi_status = PSI_START;
- player.psi_sock = sock;
- player.psi_metaint = metaint;
- NutEventPost(&player.psi_cmdevt);
- /* The next event will be the result of our start command. */
- if (NutEventWait(&player.psi_stsevt, tmo) || player.psi_status != PSI_RUNNING) {
- printf("[TOS]");
- return -1;
- }
- return 0;
- }
- #endif /* __AVR__ */
|