diff options
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/main.c | 189 | ||||
-rw-r--r-- | src/player.c | 239 | ||||
-rw-r--r-- | src/player.h | 51 |
4 files changed, 315 insertions, 174 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 6bffa2d..606430b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,10 @@ bin_PROGRAMS = pianobar -pianobar_SOURCES = main.c terminal.c terminal.h settings.c settings.h -pianobar_CPPFLAGS = ${LIBCURL_CFLAGS} ${LIBAO_CFLAGS} ${LIBXML_CFLAGS} -I../libpiano/src -pianobar_LDADD = ${LIBCURL_LIBS} ${LIBAO_LIBS} ${LIBFAAD_LIBS} ${LIBREADLINE_LIBS} ${LIBXML_LIBS} -lpthread ../libpiano/src/libpiano.la +pianobar_SOURCES = main.c terminal.c terminal.h settings.c settings.h \ + player.c player.h +pianobar_CPPFLAGS = ${LIBCURL_CFLAGS} ${LIBAO_CFLAGS} ${LIBXML_CFLAGS} \ + -I../libpiano/src +pianobar_LDADD = ${LIBCURL_LIBS} ${LIBAO_LIBS} ${LIBFAAD_LIBS} \ + ${LIBREADLINE_LIBS} ${LIBXML_LIBS} -lpthread \ + ../libpiano/src/libpiano.la dist_man1_MANS = pianobar.1 @@ -27,7 +27,6 @@ THE SOFTWARE. #include <stdlib.h> #include <string.h> #include <stdio.h> -#include <ao/ao.h> #include <neaacdec.h> #include <pthread.h> #include <unistd.h> @@ -37,175 +36,7 @@ THE SOFTWARE. #include "terminal.h" #include "settings.h" #include "config.h" - -struct aacPlayer { - /* buffer; should be large enough */ - char buffer[CURL_MAX_WRITE_SIZE*2]; - size_t bufferFilled; - /* got mdat atom */ - char dataMode; - char foundEsdsAtom; - char audioInitialized; - /* aac */ - NeAACDecHandle aacHandle; - unsigned long samplerate; - unsigned char channels; - /* audio out */ - ao_device *audioOutDevice; - char *url; - char finishedPlayback; - char doQuit; - char doPause; - CURL *audioFd; -}; - -void dumpBuffer (char *buf, size_t len) { - int i; - for (i = 0; i < len; i++) { - printf ("%02x ", buf[i] & 0xff); - } - printf ("\n"); -} - -/* FIXME: this is a huge block of _bad_ and buggy code */ -size_t playCurlCb (void *ptr, size_t size, size_t nmemb, void *stream) { - char *data = ptr; - struct aacPlayer *player = stream; - - if (player->doQuit) { - return 0; - } - - /* FIXME: not the best solution to poll every second, but the easiest one - * I know... (pthread's conditions could be another solution) */ - if (player->doPause == 1) { - curl_easy_pause (player->audioFd, CURLPAUSE_ALL); - while (player->doPause == 1) { - sleep (1); - } - curl_easy_pause (player->audioFd, CURLPAUSE_CONT); - } - - /* fill buffer */ - if (player->bufferFilled + size*nmemb > sizeof (player->buffer)) { - printf ("Buffer overflow!\n"); - return 0; - } - memcpy (player->buffer+player->bufferFilled, data, size*nmemb); - player->bufferFilled += size*nmemb; - - if (player->dataMode == 1) { - char *aacDecoded; - NeAACDecFrameInfo frameInfo; - - /* 500 is just a guessed size; prevent buffer underruns and error - * messages by faad2 */ - while (player->bufferFilled > 500) { - /* FIXME: well, i think we need this block length table */ - aacDecoded = NeAACDecDecode(player->aacHandle, &frameInfo, - (unsigned char *) player->buffer, player->bufferFilled); - if (frameInfo.error != 0) { - printf ("error: %s\n\n", - NeAACDecGetErrorMessage (frameInfo.error)); - break; - } - ao_play (player->audioOutDevice, aacDecoded, - frameInfo.samples*frameInfo.channels); - /* move remaining bytes to buffer beginning */ - memmove (player->buffer, player->buffer + frameInfo.bytesconsumed, - player->bufferFilled - frameInfo.bytesconsumed); - player->bufferFilled -= frameInfo.bytesconsumed; - } - } else { - char *searchBegin = player->buffer; - - if (!player->foundEsdsAtom) { - while (searchBegin < player->buffer + nmemb) { - if (memcmp (searchBegin, "esds", 4) == 0) { - player->foundEsdsAtom = 1; - break; - } - searchBegin++; - } - } - if (player->foundEsdsAtom && !player->audioInitialized) { - /* FIXME: is this the correct way? */ - while (searchBegin < player->buffer + nmemb) { - if (memcmp (searchBegin, "\x05\x80\x80\x80", 4) == 0) { - ao_sample_format format; - int audioOutDriver; - - /* +1+4 needs to be replaced by <something>! */ - char err = NeAACDecInit2 (player->aacHandle, - (unsigned char *) searchBegin+1+4, 5, - &player->samplerate, &player->channels); - if (err != 0) { - printf ("whoops... %i\n", err); - return 1; - } - audioOutDriver = ao_default_driver_id(); - format.bits = 16; - format.channels = player->channels; - format.rate = player->samplerate; - format.byte_format = AO_FMT_LITTLE; - player->audioOutDevice = ao_open_live (audioOutDriver, - &format, NULL); - player->audioInitialized = 1; - break; - } - searchBegin++; - } - } - if (player->audioInitialized) { - while (searchBegin < player->buffer + nmemb) { - if (memcmp (searchBegin, "mdat", 4) == 0) { - player->dataMode = 1; - memmove (player->buffer, searchBegin + strlen ("mdat"), - nmemb - (searchBegin - player->buffer)); - player->bufferFilled = nmemb - (searchBegin-player->buffer); - break; - } - searchBegin++; - } - } - if (!player->dataMode) { - /* copy last four bytes to buffer beginning and set filled - * size to four */ - memcpy (player->buffer, player->buffer+player->bufferFilled - 4, 4); - player->bufferFilled = 4; - } - } - - return size*nmemb; -} - -/* player thread; for every song a new thread is started */ -void *threadPlayUrl (void *data) { - struct aacPlayer *player = data; - NeAACDecConfigurationPtr conf; - - player->audioFd = curl_easy_init (); - player->aacHandle = NeAACDecOpen(); - - conf = NeAACDecGetCurrentConfiguration(player->aacHandle); - conf->outputFormat = FAAD_FMT_16BIT; - conf->downMatrix = 1; - NeAACDecSetConfiguration(player->aacHandle, conf); - - curl_easy_setopt (player->audioFd, CURLOPT_URL, player->url); - curl_easy_setopt (player->audioFd, CURLOPT_WRITEFUNCTION, playCurlCb); - curl_easy_setopt (player->audioFd, CURLOPT_WRITEDATA, player); - curl_easy_setopt (player->audioFd, CURLOPT_USERAGENT, PACKAGE_STRING); - curl_easy_perform (player->audioFd); - - NeAACDecClose(player->aacHandle); - ao_close(player->audioOutDevice); - curl_easy_cleanup (player->audioFd); - - player->finishedPlayback = 1; - - return NULL; -} +#include "player.h" PianoStation_t *selectStation (PianoHandle_t *ph) { PianoStation_t *curStation; @@ -534,7 +365,23 @@ int main (int argc, char **argv) { curStation->name); } break; - } + } /* end case */ + } /* end poll */ + + /* show time */ + if (player.finishedPlayback == 0) { + /* FIXME: is this calculation correct? */ + /* one frame length: T = 1/f */ + float songLength = 1.0 / (float) player.samplerate * + (float) player.channels * 1000.0 * + (float) player.sampleSizeN; + float songRemaining = songLength - 1.0 / (float) player.samplerate * + (float) player.channels * 1000.0 * + (float) player.sampleSizeCurr; + printf ("-%02i:%02i/%02i:%02i\r", (int) songRemaining/60, + (int) songRemaining%60, (int) songLength/60, + (int) songLength%60); + fflush (stdout); } } diff --git a/src/player.c b/src/player.c new file mode 100644 index 0000000..3a7aa25 --- /dev/null +++ b/src/player.c @@ -0,0 +1,239 @@ +/* +Copyright (c) 2008 Lars-Dominik Braun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include <unistd.h> +#include <string.h> + +#include "player.h" +#include "config.h" + +/* LE to BE and reverse + * @param unsigned int + * @return byteswapped unsigned int + */ +unsigned int changeByteorderUI32 (char buf[4]) { + unsigned int ret = 0; + + ret = buf[0] << 24 & 0xffffffff; + ret |= buf[1] << 16 & 0xffffff; + ret |= buf[2] << 8 & 0xffff; + ret |= buf[3] << 0 & 0xff; + return ret; +} + +/* our heart: plays streamed music + * @param streamed data + * @param block size + * @param received blocks + * @param extra data (player data) + * @return received bytes or less on error + */ +size_t playCurlCb (void *ptr, size_t size, size_t nmemb, void *stream) { + char *data = ptr; + struct aacPlayer *player = stream; + + if (player->doQuit) { + return 0; + } + + /* FIXME: not the best solution to poll every second, but the easiest + * one I know... (pthread's conditions could be another solution) */ + if (player->doPause == 1) { + curl_easy_pause (player->audioFd, CURLPAUSE_ALL); + while (player->doPause == 1) { + sleep (1); + } + curl_easy_pause (player->audioFd, CURLPAUSE_CONT); + } + + /* fill buffer */ + if (player->bufferFilled + size*nmemb > sizeof (player->buffer)) { + printf ("Buffer overflow!\n"); + return 0; + } + memcpy (player->buffer+player->bufferFilled, data, size*nmemb); + player->bufferFilled += size*nmemb; + player->bufferRead = 0; + + if (player->mode == RECV_DATA) { + char *aacDecoded; + NeAACDecFrameInfo frameInfo; + + while ((player->bufferFilled - player->bufferRead) > + player->sampleSize[player->sampleSizeCurr]) { + /* decode frame */ + aacDecoded = NeAACDecDecode(player->aacHandle, &frameInfo, + (unsigned char *) player->buffer + player->bufferRead, + player->sampleSize[player->sampleSizeCurr]); + if (frameInfo.error != 0) { + printf ("error: %s\n\n", + NeAACDecGetErrorMessage (frameInfo.error)); + break; + } + ao_play (player->audioOutDevice, aacDecoded, + frameInfo.samples*frameInfo.channels); + player->bufferRead += frameInfo.bytesconsumed; + player->sampleSizeCurr++; + } + } else { + if (player->mode == FIND_ESDS) { + while (player->bufferRead+4 < player->bufferFilled) { + if (memcmp (player->buffer + player->bufferRead, "esds", + 4) == 0) { + player->mode = FOUND_ESDS; + player->bufferRead += 4; + break; + } + player->bufferRead++; + } + } + if (player->mode == FOUND_ESDS) { + /* FIXME: is this the correct way? */ + /* we're gonna read 10 bytes */ + while (player->bufferRead+1+4+5 < player->bufferFilled) { + if (memcmp (player->buffer + player->bufferRead, + "\x05\x80\x80\x80", 4) == 0) { + ao_sample_format format; + int audioOutDriver; + + /* +1+4 needs to be replaced by <something>! */ + player->bufferRead += 1+4; + char err = NeAACDecInit2 (player->aacHandle, + (unsigned char *) player->buffer + + player->bufferRead, 5, &player->samplerate, + &player->channels); + player->bufferRead += 5; + if (err != 0) { + printf ("Error while initializing audio decoder (%i)\n", + err); + return 1; + } + audioOutDriver = ao_default_driver_id(); + format.bits = 16; + format.channels = player->channels; + format.rate = player->samplerate; + format.byte_format = AO_FMT_LITTLE; + player->audioOutDevice = ao_open_live (audioOutDriver, + &format, NULL); + player->mode = AUDIO_INITIALIZED; + break; + } + player->bufferRead++; + } + } + if (player->mode == AUDIO_INITIALIZED) { + while (player->bufferRead+4+8 < player->bufferFilled) { + if (memcmp (player->buffer + player->bufferRead, "stsz", + 4) == 0) { + player->mode = FOUND_STSZ; + player->bufferRead += 4; + /* skip version and unknown */ + player->bufferRead += 8; + break; + } + player->bufferRead++; + } + } + /* get frame sizes */ + if (player->mode == FOUND_STSZ) { + while (player->bufferRead+4 < player->bufferFilled) { + /* how many frames do we have? */ + if (player->sampleSizeN == 0) { + /* mp4 uses big endian, convert */ + player->sampleSizeN = changeByteorderUI32 (player->buffer + + player->bufferRead); + player->sampleSize = calloc (player->sampleSizeN, + sizeof (player->sampleSizeN)); + player->bufferRead += 4; + player->sampleSizeCurr = 0; + break; + } else { + player->sampleSize[player->sampleSizeCurr] = + changeByteorderUI32 (player->buffer + + player->bufferRead); + player->sampleSizeCurr++; + player->bufferRead += 4; + } + /* all sizes read, nearly ready for data mode */ + if (player->sampleSizeCurr >= player->sampleSizeN) { + player->mode = SAMPLESIZE_INITIALIZED; + break; + } + } + } + /* search for data atom and let the show begin... */ + if (player->mode == SAMPLESIZE_INITIALIZED) { + while (player->bufferRead+4 < player->bufferFilled) { + if (memcmp (player->buffer + player->bufferRead, "mdat", + 4) == 0) { + player->mode = RECV_DATA; + player->sampleSizeCurr = 0; + player->bufferRead += 4; + break; + } + player->bufferRead++; + } + } + } + + /* move remaining bytes to buffer beginning */ + memmove (player->buffer, player->buffer + player->bufferRead, + (player->bufferFilled - player->bufferRead)); + player->bufferFilled = (player->bufferFilled - player->bufferRead); + + return size*nmemb; +} + + +/* player thread; for every song a new thread is started + * @param aacPlayer structure + * @return NULL NULL NULL ... + */ +void *threadPlayUrl (void *data) { + struct aacPlayer *player = data; + NeAACDecConfigurationPtr conf; + + player->audioFd = curl_easy_init (); + player->aacHandle = NeAACDecOpen(); + + conf = NeAACDecGetCurrentConfiguration(player->aacHandle); + conf->outputFormat = FAAD_FMT_16BIT; + conf->downMatrix = 1; + NeAACDecSetConfiguration(player->aacHandle, conf); + + curl_easy_setopt (player->audioFd, CURLOPT_URL, player->url); + curl_easy_setopt (player->audioFd, CURLOPT_WRITEFUNCTION, playCurlCb); + curl_easy_setopt (player->audioFd, CURLOPT_WRITEDATA, player); + curl_easy_setopt (player->audioFd, CURLOPT_USERAGENT, PACKAGE_STRING); + curl_easy_perform (player->audioFd); + + NeAACDecClose(player->aacHandle); + ao_close(player->audioOutDevice); + curl_easy_cleanup (player->audioFd); + if (player->sampleSize != NULL) { + free (player->sampleSize); + } + + player->finishedPlayback = 1; + + return NULL; +} diff --git a/src/player.h b/src/player.h new file mode 100644 index 0000000..2e36c11 --- /dev/null +++ b/src/player.h @@ -0,0 +1,51 @@ +/* +Copyright (c) 2008 Lars-Dominik Braun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include <curl/curl.h> +#include <neaacdec.h> +#include <ao/ao.h> + +struct aacPlayer { + /* buffer; should be large enough */ + char buffer[CURL_MAX_WRITE_SIZE*2]; + size_t bufferFilled; + size_t bufferRead; + enum {FIND_ESDS, FOUND_ESDS, AUDIO_INITIALIZED, FOUND_STSZ, + SAMPLESIZE_INITIALIZED, RECV_DATA} mode; + /* stsz atom: sample sizes */ + unsigned int *sampleSize; + size_t sampleSizeN; + size_t sampleSizeCurr; + /* aac */ + NeAACDecHandle aacHandle; + unsigned long samplerate; + unsigned char channels; + /* audio out */ + ao_device *audioOutDevice; + char *url; + char finishedPlayback; + char doQuit; + char doPause; + CURL *audioFd; +}; + +void *threadPlayUrl (void *data); |