diff options
Diffstat (limited to 'src/player.c')
-rw-r--r-- | src/player.c | 239 |
1 files changed, 239 insertions, 0 deletions
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; +} |