diff options
Diffstat (limited to 'trunk/src/decode.c')
-rw-r--r-- | trunk/src/decode.c | 706 |
1 files changed, 0 insertions, 706 deletions
diff --git a/trunk/src/decode.c b/trunk/src/decode.c deleted file mode 100644 index 82eba19b9..000000000 --- a/trunk/src/decode.c +++ /dev/null @@ -1,706 +0,0 @@ -/* the Music Player Daemon (MPD) - * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) - * This project's homepage is: http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "decode.h" - -#include "player.h" -#include "playerData.h" -#include "utils.h" -#include "pcm_utils.h" -#include "audio.h" -#include "path.h" -#include "log.h" -#include "sig_handlers.h" -#include "ls.h" -#include "utf8.h" - -#include <signal.h> -#include <sys/types.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <sys/wait.h> -#include <unistd.h> -#include <string.h> - -static int decode_pid; - -void decodeSigHandler(int sig, siginfo_t * si, void *v) -{ - if (sig == SIGCHLD) { - int status; - if (decode_pid == wait3(&status, WNOHANG, NULL)) { - if (WIFSIGNALED(status)) { - if (WTERMSIG(status) != SIGTERM) { - ERROR("decode process died from " - "signal: %i\n", WTERMSIG(status)); - } - } - decode_pid = 0; - getPlayerData()->playerControl.decode_pid = 0; - } - } else if (sig == SIGTERM) { - int pid = decode_pid; - if (pid > 0) { - DEBUG("player (or child) got SIGTERM\n"); - kill(pid, SIGTERM); - } else - DEBUG("decoder (or child) got SIGTERM\n"); - exit(EXIT_SUCCESS); - } -} - -static void stopDecode(DecoderControl * dc) -{ - if (decode_pid > 0 && (dc->start || dc->state != DECODE_STATE_STOP)) { - dc->stop = 1; - while (decode_pid > 0 && dc->stop) - my_usleep(10000); - } -} - -static void quitDecode(PlayerControl * pc, DecoderControl * dc) -{ - stopDecode(dc); - pc->state = PLAYER_STATE_STOP; - dc->seek = 0; - pc->play = 0; - pc->stop = 0; - pc->pause = 0; - kill(getppid(), SIGUSR1); -} - -static int calculateCrossFadeChunks(PlayerControl * pc, AudioFormat * af) -{ - long chunks; - - if (pc->crossFade <= 0) - return 0; - - chunks = (af->sampleRate * af->bits * af->channels / 8.0 / CHUNK_SIZE); - chunks = (chunks * pc->crossFade + 0.5); - - if (chunks > (buffered_chunks - buffered_before_play)) { - chunks = buffered_chunks - buffered_before_play; - } - - if (chunks < 0) - chunks = 0; - - return (int)chunks; -} - -#define handleDecodeStart() \ - if(decodeWaitedOn) { \ - if(dc->state!=DECODE_STATE_START && decode_pid > 0 && \ - dc->error==DECODE_ERROR_NOERROR) \ - { \ - decodeWaitedOn = 0; \ - if(openAudioDevice(&(cb->audioFormat))<0) { \ - pathcpy_trunc(pc->erroredUrl, pc->utf8url); \ - pc->error = PLAYER_ERROR_AUDIO; \ - ERROR("problems opening audio device while playing \"%s\"\n", pc->utf8url); \ - quitDecode(pc,dc); \ - return; \ - } \ - pc->totalTime = dc->totalTime; \ - pc->sampleRate = dc->audioFormat.sampleRate; \ - pc->bits = dc->audioFormat.bits; \ - pc->channels = dc->audioFormat.channels; \ - sizeToTime = 8.0/cb->audioFormat.bits/ \ - cb->audioFormat.channels/ \ - cb->audioFormat.sampleRate; \ - } \ - else if(dc->state!=DECODE_STATE_START || decode_pid <= 0) { \ - pathcpy_trunc(pc->erroredUrl, pc->utf8url); \ - pc->error = PLAYER_ERROR_FILE; \ - quitDecode(pc,dc); \ - return; \ - } \ - else { \ - my_usleep(10000); \ - continue; \ - } \ - } - -static int waitOnDecode(PlayerControl * pc, DecoderControl * dc, - OutputBuffer * cb, int *decodeWaitedOn) -{ - MpdTag *tag = NULL; - pathcpy_trunc(pc->currentUrl, pc->utf8url); - - while (decode_pid > 0 && dc->start) - my_usleep(10000); - - if (dc->start || dc->error != DECODE_ERROR_NOERROR) { - pathcpy_trunc(pc->erroredUrl, pc->utf8url); - pc->error = PLAYER_ERROR_FILE; - quitDecode(pc, dc); - return -1; - } - - if ((tag = metadataChunkToMpdTagDup(&(pc->fileMetadataChunk)))) { - sendMetadataToAudioDevice(tag); - freeMpdTag(tag); - } - - pc->totalTime = pc->fileTime; - pc->bitRate = 0; - pc->sampleRate = 0; - pc->bits = 0; - pc->channels = 0; - *decodeWaitedOn = 1; - - return 0; -} - -static int decodeSeek(PlayerControl * pc, DecoderControl * dc, - OutputBuffer * cb, int *decodeWaitedOn, int *next) -{ - int ret = -1; - - if (decode_pid > 0) { - if (dc->state == DECODE_STATE_STOP || dc->error || - strcmp(dc->utf8url, pc->utf8url) != 0) { - stopDecode(dc); - *next = -1; - cb->begin = 0; - cb->end = 0; - dc->error = 0; - dc->start = 1; - waitOnDecode(pc, dc, cb, decodeWaitedOn); - } - if (decode_pid > 0 && dc->state != DECODE_STATE_STOP && - dc->seekable) { - *next = -1; - dc->seekWhere = pc->seekWhere > pc->totalTime - 0.1 ? - pc->totalTime - 0.1 : pc->seekWhere; - dc->seekWhere = 0 > dc->seekWhere ? 0 : dc->seekWhere; - dc->seekError = 0; - dc->seek = 1; - while (decode_pid > 0 && dc->seek) - my_usleep(10000); - if (!dc->seekError) { - pc->elapsedTime = dc->seekWhere; - ret = 0; - } - } - } - pc->seek = 0; - - return ret; -} - -#define processDecodeInput() \ - if(pc->cycleLogFiles) { \ - cycle_log_files(); \ - pc->cycleLogFiles = 0; \ - } \ - if(pc->lockQueue) { \ - pc->queueLockState = PLAYER_QUEUE_LOCKED; \ - pc->lockQueue = 0; \ - } \ - if(pc->unlockQueue) { \ - pc->queueLockState = PLAYER_QUEUE_UNLOCKED; \ - pc->unlockQueue = 0; \ - } \ - if(pc->pause) { \ - pause = !pause; \ - if (pause) pc->state = PLAYER_STATE_PAUSE; \ - else { \ - if (openAudioDevice(NULL) >= 0) pc->state = PLAYER_STATE_PLAY; \ - else { \ - pathcpy_trunc(pc->erroredUrl, pc->utf8url); \ - pc->error = PLAYER_ERROR_AUDIO; \ - ERROR("problems opening audio device while playing \"%s\"\n", pc->utf8url); \ - pause = -1; \ - } \ - } \ - pc->pause = 0; \ - kill(getppid(), SIGUSR1); \ - if (pause == -1) pause = 1; \ - else if (pause) { \ - dropBufferedAudio(); \ - closeAudioDevice(); \ - } \ - } \ - if(pc->seek) { \ - dropBufferedAudio(); \ - if(decodeSeek(pc,dc,cb,&decodeWaitedOn,&next) == 0) { \ - doCrossFade = 0; \ - nextChunk = -1; \ - bbp = 0; \ - } \ - } \ - if(pc->stop) { \ - dropBufferedAudio(); \ - quitDecode(pc,dc); \ - return; \ - } - -static void decodeStart(PlayerControl * pc, OutputBuffer * cb, - DecoderControl * dc) -{ - int ret; - InputStream inStream; - InputPlugin *plugin = NULL; - char *path; - - if (isRemoteUrl(pc->utf8url)) - path = utf8StrToLatin1Dup(pc->utf8url); - else - path = xstrdup(rmp2amp(utf8ToFsCharset(pc->utf8url))); - - if (!path) { - dc->error = DECODE_ERROR_FILE; - dc->state = DECODE_STATE_STOP; - dc->start = 0; - return; - } - - copyMpdTagToOutputBuffer(cb, NULL); - - pathcpy_trunc(dc->utf8url, pc->utf8url); - - if (openInputStream(&inStream, path) < 0) { - dc->error = DECODE_ERROR_FILE; - dc->state = DECODE_STATE_STOP; - dc->start = 0; - free(path); - return; - } - - dc->state = DECODE_STATE_START; - dc->start = 0; - - while (!inputStreamAtEOF(&inStream) && bufferInputStream(&inStream) < 0 - && !dc->stop) { - /* sleep so we don't consume 100% of the cpu */ - my_usleep(1000); - } - - /* for http streams, seekable is determined in bufferInputStream */ - dc->seekable = inStream.seekable; - - if (dc->stop) { - dc->state = DECODE_STATE_STOP; - dc->stop = 0; - free(path); - return; - } - - /*if(inStream.metaName) { - MpdTag * tag = newMpdTag(); - tag->name = xstrdup(inStream.metaName); - copyMpdTagToOutputBuffer(cb, tag); - freeMpdTag(tag); - } */ - - /* reset Metadata in OutputBuffer */ - - ret = DECODE_ERROR_UNKTYPE; - if (isRemoteUrl(dc->utf8url)) { - unsigned int next = 0; - cb->acceptMetadata = 1; - - /* first we try mime types: */ - while (ret - && (plugin = - getInputPluginFromMimeType(inStream.mime, next++))) { - if (!plugin->streamDecodeFunc) - continue; - if (!(plugin->streamTypes & INPUT_PLUGIN_STREAM_URL)) - continue; - if (plugin->tryDecodeFunc - && !plugin->tryDecodeFunc(&inStream)) - continue; - ret = plugin->streamDecodeFunc(cb, dc, &inStream); - break; - } - - /* if that fails, try suffix matching the URL: */ - if (plugin == NULL) { - char *s = getSuffix(dc->utf8url); - next = 0; - while (ret - && (plugin = - getInputPluginFromSuffix(s, next++))) { - if (!plugin->streamDecodeFunc) - continue; - if (!(plugin->streamTypes & - INPUT_PLUGIN_STREAM_URL)) - continue; - if (plugin->tryDecodeFunc && - !plugin->tryDecodeFunc(&inStream)) - continue; - ret = - plugin->streamDecodeFunc(cb, dc, &inStream); - break; - } - } - /* fallback to mp3: */ - /* this is needed for bastard streams that don't have a suffix - or set the mimeType */ - if (plugin == NULL) { - /* we already know our mp3Plugin supports streams, no - * need to check for stream{Types,DecodeFunc} */ - if ((plugin = getInputPluginFromName("mp3"))) - ret = plugin->streamDecodeFunc(cb, dc, - &inStream); - } - } else { - unsigned int next = 0; - char *s = getSuffix(dc->utf8url); - cb->acceptMetadata = 0; - while (ret && (plugin = getInputPluginFromSuffix(s, next++))) { - if (!plugin->streamTypes & INPUT_PLUGIN_STREAM_FILE) - continue; - if (plugin->tryDecodeFunc - && !plugin->tryDecodeFunc(&inStream)) - continue; - - if (plugin->streamDecodeFunc) { - ret = - plugin->streamDecodeFunc(cb, dc, &inStream); - break; - } else if (plugin->fileDecodeFunc) { - closeInputStream(&inStream); - ret = plugin->fileDecodeFunc(cb, dc, path); - } - } - } - - if (ret < 0 || ret == DECODE_ERROR_UNKTYPE) { - pathcpy_trunc(pc->erroredUrl, dc->utf8url); - if (ret != DECODE_ERROR_UNKTYPE) - dc->error = DECODE_ERROR_FILE; - else { - dc->error = DECODE_ERROR_UNKTYPE; - closeInputStream(&inStream); - } - dc->stop = 0; - dc->state = DECODE_STATE_STOP; - } - - free(path); -} - -static int decoderInit(PlayerControl * pc, OutputBuffer * cb, - DecoderControl * dc) -{ - blockSignals(); - getPlayerData()->playerControl.decode_pid = 0; - decode_pid = fork(); - - if (decode_pid == 0) { - /* CHILD */ - unblockSignals(); - - while (1) { - if (dc->cycleLogFiles) { - cycle_log_files(); - dc->cycleLogFiles = 0; - } else if (dc->start || dc->seek) - decodeStart(pc, cb, dc); - else if (dc->stop) { - dc->state = DECODE_STATE_STOP; - dc->stop = 0; - } else - my_usleep(10000); - } - - exit(EXIT_SUCCESS); - /* END OF CHILD */ - } else if (decode_pid < 0) { - unblockSignals(); - pathcpy_trunc(pc->erroredUrl, pc->utf8url); - pc->error = PLAYER_ERROR_SYSTEM; - return -1; - } - DEBUG("decoder PID: %d\n", decode_pid); - getPlayerData()->playerControl.decode_pid = decode_pid; - unblockSignals(); - - return 0; -} - -static void handleMetadata(OutputBuffer * cb, PlayerControl * pc, int *previous, - int *currentChunkSent, MetadataChunk * currentChunk) -{ - if (cb->begin != cb->end) { - int meta = cb->metaChunk[cb->begin]; - if (meta != *previous) { - DEBUG("player: metadata change\n"); - if (meta >= 0 && cb->metaChunkSet[meta]) { - DEBUG("player: new metadata from decoder!\n"); - memcpy(currentChunk, - cb->metadataChunks + meta, - sizeof(MetadataChunk)); - *currentChunkSent = 0; - cb->metaChunkSet[meta] = 0; - } - } - *previous = meta; - } - if (!(*currentChunkSent) && pc->metadataState == - PLAYER_METADATA_STATE_WRITE) { - MpdTag *tag = NULL; - - *currentChunkSent = 1; - - if ((tag = metadataChunkToMpdTagDup(currentChunk))) { - sendMetadataToAudioDevice(tag); - freeMpdTag(tag); - } - - memcpy(&(pc->metadataChunk), currentChunk, - sizeof(MetadataChunk)); - pc->metadataState = PLAYER_METADATA_STATE_READ; - kill(getppid(), SIGUSR1); - } -} - -static void advanceOutputBufferTo(OutputBuffer * cb, PlayerControl * pc, - int *previous, int *currentChunkSent, - MetadataChunk * currentChunk, int to) -{ - while (cb->begin != to) { - handleMetadata(cb, pc, previous, currentChunkSent, - currentChunk); - if (cb->begin + 1 >= buffered_chunks) { - cb->begin = 0; - } - else cb->begin++; - } -} - -static void decodeParent(PlayerControl * pc, DecoderControl * dc, OutputBuffer * cb) -{ - int pause = 0; - int quit = 0; - int bbp = buffered_before_play; - int doCrossFade = 0; - int crossFadeChunks = 0; - int fadePosition; - int nextChunk = -1; - int test; - int decodeWaitedOn = 0; - char silence[CHUNK_SIZE]; - double sizeToTime = 0.0; - int previousMetadataChunk = -1; - MetadataChunk currentMetadataChunk; - int currentChunkSent = 1; - int end; - int next = -1; - - memset(silence, 0, CHUNK_SIZE); - - if (waitOnDecode(pc, dc, cb, &decodeWaitedOn) < 0) - return; - - pc->elapsedTime = 0; - pc->state = PLAYER_STATE_PLAY; - pc->play = 0; - kill(getppid(), SIGUSR1); - - while (decode_pid > 0 && - cb->end - cb->begin < bbp && - cb->end != buffered_chunks - 1 && - dc->state != DECODE_STATE_STOP) { - processDecodeInput(); - my_usleep(1000); - } - - while (!quit) { - processDecodeInput(); - handleDecodeStart(); - handleMetadata(cb, pc, &previousMetadataChunk, - ¤tChunkSent, ¤tMetadataChunk); - if (dc->state == DECODE_STATE_STOP && - pc->queueState == PLAYER_QUEUE_FULL && - pc->queueLockState == PLAYER_QUEUE_UNLOCKED) { - next = cb->end; - dc->start = 1; - pc->queueState = PLAYER_QUEUE_DECODE; - kill(getppid(), SIGUSR1); - } - if (next >= 0 && doCrossFade == 0 && !dc->start && - dc->state != DECODE_STATE_START) { - nextChunk = -1; - if (isCurrentAudioFormat(&(cb->audioFormat))) { - doCrossFade = 1; - crossFadeChunks = - calculateCrossFadeChunks(pc, - &(cb-> - audioFormat)); - if (!crossFadeChunks - || pc->crossFade >= dc->totalTime) { - doCrossFade = -1; - } - } else - doCrossFade = -1; - } - - /* copy these to local variables to prevent any potential - race conditions and weirdness */ - end = cb->end; - - if (pause) - my_usleep(10000); - else if (cb->begin != end && cb->begin != next) { - if (doCrossFade == 1 && next >= 0 && - ((next > cb->begin && - (fadePosition = next - cb->begin) - <= crossFadeChunks) || - (cb->begin > next && - (fadePosition = next - cb->begin + - buffered_chunks) <= crossFadeChunks))) { - if (nextChunk < 0) { - crossFadeChunks = fadePosition; - } - test = end; - if (end < cb->begin) - test += buffered_chunks; - nextChunk = cb->begin + crossFadeChunks; - if (nextChunk < test) { - if (nextChunk >= buffered_chunks) { - nextChunk -= buffered_chunks; - } - pcm_mix(cb->chunks + - cb->begin * CHUNK_SIZE, - cb->chunks + - nextChunk * CHUNK_SIZE, - cb->chunkSize[cb->begin], - cb->chunkSize[nextChunk], - &(cb->audioFormat), - ((float)fadePosition) / - crossFadeChunks); - if (cb->chunkSize[nextChunk] > - cb->chunkSize[cb->begin] - ) { - cb->chunkSize[cb->begin] - = cb->chunkSize[nextChunk]; - } - } else { - if (dc->state == DECODE_STATE_STOP) { - doCrossFade = -1; - } else - continue; - } - } - pc->elapsedTime = cb->times[cb->begin]; - pc->bitRate = cb->bitRate[cb->begin]; - pcm_volumeChange(cb->chunks + cb->begin * - CHUNK_SIZE, - cb->chunkSize[cb->begin], - &(cb->audioFormat), - pc->softwareVolume); - if (playAudio(cb->chunks + cb->begin * CHUNK_SIZE, - cb->chunkSize[cb->begin]) < 0) { - quit = 1; - } - pc->totalPlayTime += - sizeToTime * cb->chunkSize[cb->begin]; - if (cb->begin + 1 >= buffered_chunks) { - cb->begin = 0; - } else - cb->begin++; - } else if (cb->begin != end && cb->begin == next) { - if (doCrossFade == 1 && nextChunk >= 0) { - nextChunk = cb->begin + crossFadeChunks; - test = end; - if (end < cb->begin) - test += buffered_chunks; - if (nextChunk < test) { - if (nextChunk >= buffered_chunks) { - nextChunk -= buffered_chunks; - } - advanceOutputBufferTo(cb, pc, - &previousMetadataChunk, - ¤tChunkSent, - ¤tMetadataChunk, - nextChunk); - } - } - while (pc->queueState == PLAYER_QUEUE_DECODE || - pc->queueLockState == PLAYER_QUEUE_LOCKED) { - processDecodeInput(); - if (quit) { - quitDecode(pc, dc); - return; - } - my_usleep(10000); - } - if (pc->queueState != PLAYER_QUEUE_PLAY) { - quit = 1; - break; - } else { - next = -1; - if (waitOnDecode(pc, dc, cb, &decodeWaitedOn) < - 0) { - return; - } - nextChunk = -1; - doCrossFade = 0; - crossFadeChunks = 0; - pc->queueState = PLAYER_QUEUE_EMPTY; - kill(getppid(), SIGUSR1); - } - } else if (decode_pid <= 0 || - (dc->state == DECODE_STATE_STOP && !dc->start)) { - quit = 1; - break; - } else { - /*DEBUG("waiting for decoded audio, play silence\n");*/ - if (playAudio(silence, CHUNK_SIZE) < 0) - quit = 1; - } - } - - quitDecode(pc, dc); -} - -/* decode w/ buffering - * this will fork another process - * child process does decoding - * parent process does playing audio - */ -void decode(void) -{ - OutputBuffer *cb; - PlayerControl *pc; - DecoderControl *dc; - - cb = &(getPlayerData()->buffer); - - clearAllMetaChunkSets(cb); - cb->begin = 0; - cb->end = 0; - pc = &(getPlayerData()->playerControl); - dc = &(getPlayerData()->decoderControl); - dc->error = 0; - dc->seek = 0; - dc->stop = 0; - dc->start = 1; - - if (decode_pid <= 0) { - if (decoderInit(pc, cb, dc) < 0) - return; - } - - decodeParent(pc, dc, cb); -} |