2004-02-24 00:41:20 +01:00
|
|
|
/* the Music Player Daemon (MPD)
|
2007-04-05 05:22:33 +02:00
|
|
|
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
|
2004-02-24 00:41:20 +01:00
|
|
|
* 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"
|
2004-02-27 02:35:23 +01:00
|
|
|
|
2004-02-24 00:41:20 +01:00
|
|
|
#include "player.h"
|
|
|
|
#include "playerData.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "pcm_utils.h"
|
|
|
|
#include "audio.h"
|
|
|
|
#include "path.h"
|
|
|
|
#include "log.h"
|
2004-04-11 20:27:12 +02:00
|
|
|
#include "sig_handlers.h"
|
2004-05-18 15:13:55 +02:00
|
|
|
#include "ls.h"
|
2004-06-03 01:22:37 +02:00
|
|
|
#include "utf8.h"
|
2008-01-03 08:29:49 +01:00
|
|
|
#include "os_compat.h"
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2007-01-14 04:07:53 +01:00
|
|
|
static int decode_pid;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
void decodeSigHandler(int sig, siginfo_t * si, void *v)
|
|
|
|
{
|
|
|
|
if (sig == SIGCHLD) {
|
2004-02-24 00:41:20 +01:00
|
|
|
int status;
|
2006-07-20 18:02:40 +02:00
|
|
|
if (decode_pid == wait3(&status, WNOHANG, NULL)) {
|
2007-08-27 23:18:35 +02:00
|
|
|
/*
|
2006-07-20 18:02:40 +02:00
|
|
|
if (WIFSIGNALED(status)) {
|
|
|
|
if (WTERMSIG(status) != SIGTERM) {
|
2005-03-09 04:13:33 +01:00
|
|
|
ERROR("decode process died from "
|
2006-07-20 18:02:40 +02:00
|
|
|
"signal: %i\n", WTERMSIG(status));
|
2005-03-09 04:13:33 +01:00
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2007-08-27 23:18:35 +02:00
|
|
|
*/
|
2005-03-09 04:13:33 +01:00
|
|
|
decode_pid = 0;
|
|
|
|
getPlayerData()->playerControl.decode_pid = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
} else if (sig == SIGTERM) {
|
2005-03-09 04:13:33 +01:00
|
|
|
int pid = decode_pid;
|
2006-07-20 18:02:40 +02:00
|
|
|
if (pid > 0) {
|
2007-08-27 23:18:35 +02:00
|
|
|
/* DEBUG("player (or child) got SIGTERM\n"); */
|
2007-06-07 22:02:27 +02:00
|
|
|
kill(pid, SIGCONT);
|
2006-07-20 18:02:40 +02:00
|
|
|
kill(pid, SIGTERM);
|
2007-08-27 23:18:35 +02:00
|
|
|
} /* else
|
|
|
|
DEBUG("decoder (or child) got SIGTERM\n"); */
|
2004-04-03 01:34:16 +02:00
|
|
|
exit(EXIT_SUCCESS);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void stopDecode(DecoderControl * dc)
|
|
|
|
{
|
|
|
|
if (decode_pid > 0 && (dc->start || dc->state != DECODE_STATE_STOP)) {
|
2004-02-24 00:41:20 +01:00
|
|
|
dc->stop = 1;
|
2007-09-06 03:07:49 +02:00
|
|
|
kill(decode_pid, SIGCONT);
|
2008-03-26 11:38:58 +01:00
|
|
|
signalNotify(&(getPlayerData()->buffer.notify));
|
2006-07-20 18:02:40 +02:00
|
|
|
while (decode_pid > 0 && dc->stop)
|
|
|
|
my_usleep(10000);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void quitDecode(PlayerControl * pc, DecoderControl * dc)
|
|
|
|
{
|
2007-06-05 00:29:55 +02:00
|
|
|
int pid;
|
|
|
|
|
2004-02-24 00:41:20 +01:00
|
|
|
stopDecode(dc);
|
|
|
|
pc->state = PLAYER_STATE_STOP;
|
2006-07-14 21:52:29 +02:00
|
|
|
dc->seek = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
pc->play = 0;
|
|
|
|
pc->stop = 0;
|
|
|
|
pc->pause = 0;
|
2007-06-05 00:29:55 +02:00
|
|
|
|
|
|
|
pid = decode_pid;
|
|
|
|
if (pid > 0)
|
|
|
|
kill(pid, SIGSTOP);
|
2007-06-05 00:36:39 +02:00
|
|
|
|
|
|
|
kill(getppid(), SIGUSR1);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static int calculateCrossFadeChunks(PlayerControl * pc, AudioFormat * af)
|
|
|
|
{
|
2004-03-05 02:29:08 +01:00
|
|
|
long chunks;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (pc->crossFade <= 0)
|
|
|
|
return 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
chunks = (af->sampleRate * af->bits * af->channels / 8.0 / CHUNK_SIZE);
|
|
|
|
chunks = (chunks * pc->crossFade + 0.5);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (chunks > (buffered_chunks - buffered_before_play)) {
|
|
|
|
chunks = buffered_chunks - buffered_before_play;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (chunks < 0)
|
|
|
|
chunks = 0;
|
2004-02-25 18:53:48 +01:00
|
|
|
|
2004-03-05 02:29:08 +01:00
|
|
|
return (int)chunks;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2004-05-19 04:14:20 +02:00
|
|
|
#define handleDecodeStart() \
|
2006-07-14 21:52:29 +02:00
|
|
|
if(decodeWaitedOn) { \
|
|
|
|
if(dc->state!=DECODE_STATE_START && decode_pid > 0 && \
|
|
|
|
dc->error==DECODE_ERROR_NOERROR) \
|
|
|
|
{ \
|
|
|
|
decodeWaitedOn = 0; \
|
|
|
|
if(openAudioDevice(&(cb->audioFormat))<0) { \
|
2006-07-30 12:31:41 +02:00
|
|
|
pathcpy_trunc(pc->erroredUrl, pc->utf8url); \
|
2006-07-14 21:52:29 +02:00
|
|
|
pc->error = PLAYER_ERROR_AUDIO; \
|
2004-11-03 03:11:08 +01:00
|
|
|
ERROR("problems opening audio device while playing \"%s\"\n", pc->utf8url); \
|
2006-07-14 21:52:29 +02:00
|
|
|
quitDecode(pc,dc); \
|
|
|
|
return; \
|
2007-09-06 03:07:49 +02:00
|
|
|
} else if (decode_pid > 0) { \
|
|
|
|
kill(decode_pid, SIGCONT); }\
|
2007-08-12 21:23:48 +02:00
|
|
|
if (pause) { \
|
|
|
|
dropBufferedAudio(); \
|
|
|
|
closeAudioDevice(); \
|
|
|
|
} \
|
2006-07-14 21:52:29 +02:00
|
|
|
pc->totalTime = dc->totalTime; \
|
|
|
|
pc->sampleRate = dc->audioFormat.sampleRate; \
|
|
|
|
pc->bits = dc->audioFormat.bits; \
|
|
|
|
pc->channels = dc->audioFormat.channels; \
|
2004-06-03 14:34:25 +02:00
|
|
|
sizeToTime = 8.0/cb->audioFormat.bits/ \
|
|
|
|
cb->audioFormat.channels/ \
|
|
|
|
cb->audioFormat.sampleRate; \
|
2004-05-20 05:44:33 +02:00
|
|
|
} \
|
2005-03-09 04:13:33 +01:00
|
|
|
else if(dc->state!=DECODE_STATE_START || decode_pid <= 0) { \
|
2006-07-30 12:31:41 +02:00
|
|
|
pathcpy_trunc(pc->erroredUrl, pc->utf8url); \
|
2004-05-20 05:44:33 +02:00
|
|
|
pc->error = PLAYER_ERROR_FILE; \
|
2004-05-19 04:14:20 +02:00
|
|
|
quitDecode(pc,dc); \
|
|
|
|
return; \
|
2004-05-20 05:44:33 +02:00
|
|
|
} \
|
|
|
|
else { \
|
2004-06-20 00:27:29 +02:00
|
|
|
my_usleep(10000); \
|
2004-05-20 05:44:33 +02:00
|
|
|
continue; \
|
2006-07-14 21:52:29 +02:00
|
|
|
} \
|
|
|
|
}
|
2004-05-19 04:14:20 +02:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static int waitOnDecode(PlayerControl * pc, DecoderControl * dc,
|
|
|
|
OutputBuffer * cb, int *decodeWaitedOn)
|
2004-05-19 04:14:20 +02:00
|
|
|
{
|
2006-07-20 18:02:40 +02:00
|
|
|
MpdTag *tag = NULL;
|
2006-07-30 12:31:41 +02:00
|
|
|
pathcpy_trunc(pc->currentUrl, pc->utf8url);
|
2004-06-01 01:29:35 +02:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
while (decode_pid > 0 && dc->start)
|
|
|
|
my_usleep(10000);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (dc->start || dc->error != DECODE_ERROR_NOERROR) {
|
2006-07-30 12:31:41 +02:00
|
|
|
pathcpy_trunc(pc->erroredUrl, pc->utf8url);
|
2004-02-24 00:41:20 +01:00
|
|
|
pc->error = PLAYER_ERROR_FILE;
|
2006-07-20 18:02:40 +02:00
|
|
|
quitDecode(pc, dc);
|
2004-02-24 00:41:20 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if ((tag = metadataChunkToMpdTagDup(&(pc->fileMetadataChunk)))) {
|
2004-10-26 04:13:13 +02:00
|
|
|
sendMetadataToAudioDevice(tag);
|
|
|
|
freeMpdTag(tag);
|
|
|
|
}
|
2004-10-25 22:09:03 +02:00
|
|
|
|
2006-07-14 21:52:29 +02:00
|
|
|
pc->totalTime = pc->fileTime;
|
|
|
|
pc->bitRate = 0;
|
|
|
|
pc->sampleRate = 0;
|
|
|
|
pc->bits = 0;
|
|
|
|
pc->channels = 0;
|
|
|
|
*decodeWaitedOn = 1;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static int decodeSeek(PlayerControl * pc, DecoderControl * dc,
|
|
|
|
OutputBuffer * cb, int *decodeWaitedOn, int *next)
|
2004-05-19 04:14:20 +02:00
|
|
|
{
|
2006-07-14 21:52:29 +02:00
|
|
|
int ret = -1;
|
2004-05-19 04:14:20 +02:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (decode_pid > 0) {
|
|
|
|
if (dc->state == DECODE_STATE_STOP || dc->error ||
|
|
|
|
strcmp(dc->utf8url, pc->utf8url) != 0) {
|
2004-02-24 00:41:20 +01:00
|
|
|
stopDecode(dc);
|
2004-06-10 21:48:53 +02:00
|
|
|
*next = -1;
|
2004-03-23 02:12:30 +01:00
|
|
|
cb->begin = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
cb->end = 0;
|
|
|
|
dc->error = 0;
|
|
|
|
dc->start = 1;
|
2006-07-20 18:02:40 +02:00
|
|
|
waitOnDecode(pc, dc, cb, decodeWaitedOn);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
if (decode_pid > 0 && dc->state != DECODE_STATE_STOP &&
|
|
|
|
dc->seekable) {
|
2004-06-10 21:48:53 +02:00
|
|
|
*next = -1;
|
2006-07-20 18:02:40 +02:00
|
|
|
dc->seekWhere = pc->seekWhere > pc->totalTime - 0.1 ?
|
|
|
|
pc->totalTime - 0.1 : pc->seekWhere;
|
2004-03-18 23:20:26 +01:00
|
|
|
dc->seekWhere = 0 > dc->seekWhere ? 0 : dc->seekWhere;
|
2006-07-14 21:52:29 +02:00
|
|
|
dc->seekError = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
dc->seek = 1;
|
2007-09-07 03:17:09 +02:00
|
|
|
kill(decode_pid, SIGCONT);
|
2008-03-26 11:38:58 +01:00
|
|
|
signalNotify(&(getPlayerData()->buffer.notify));
|
2006-07-20 18:02:40 +02:00
|
|
|
while (decode_pid > 0 && dc->seek)
|
|
|
|
my_usleep(10000);
|
|
|
|
if (!dc->seekError) {
|
2006-07-14 21:52:29 +02:00
|
|
|
pc->elapsedTime = dc->seekWhere;
|
|
|
|
ret = 0;
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pc->seek = 0;
|
2004-05-19 04:14:20 +02:00
|
|
|
|
2006-07-14 21:52:29 +02:00
|
|
|
return ret;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#define processDecodeInput() \
|
2004-04-15 07:07:04 +02:00
|
|
|
if(pc->cycleLogFiles) { \
|
2006-08-01 06:18:53 +02:00
|
|
|
cycle_log_files(); \
|
2004-04-15 07:07:04 +02:00
|
|
|
pc->cycleLogFiles = 0; \
|
|
|
|
} \
|
2004-02-24 00:41:20 +01:00
|
|
|
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; \
|
2007-08-12 21:15:06 +02:00
|
|
|
if (pause) { \
|
|
|
|
pc->state = PLAYER_STATE_PAUSE; \
|
|
|
|
} else { \
|
|
|
|
if (openAudioDevice(NULL) >= 0) { \
|
2007-09-06 03:07:49 +02:00
|
|
|
if (decode_pid > 0) \
|
|
|
|
kill(decode_pid, SIGCONT); \
|
2007-08-12 21:15:06 +02:00
|
|
|
pc->state = PLAYER_STATE_PLAY; \
|
|
|
|
} else { \
|
2006-07-30 12:31:41 +02:00
|
|
|
pathcpy_trunc(pc->erroredUrl, pc->utf8url); \
|
2004-03-20 20:47:05 +01:00
|
|
|
pc->error = PLAYER_ERROR_AUDIO; \
|
2004-11-03 03:11:08 +01:00
|
|
|
ERROR("problems opening audio device while playing \"%s\"\n", pc->utf8url); \
|
2007-04-03 22:22:10 +02:00
|
|
|
pause = -1; \
|
2004-03-20 20:47:05 +01:00
|
|
|
} \
|
|
|
|
} \
|
2004-02-24 00:41:20 +01:00
|
|
|
pc->pause = 0; \
|
2007-04-03 22:10:22 +02:00
|
|
|
kill(getppid(), SIGUSR1); \
|
2007-08-12 21:15:06 +02:00
|
|
|
if (pause == -1) { \
|
|
|
|
pause = 1; \
|
|
|
|
} else if (pause) { \
|
2005-12-17 01:56:48 +01:00
|
|
|
dropBufferedAudio(); \
|
|
|
|
closeAudioDevice(); \
|
|
|
|
} \
|
2004-02-24 00:41:20 +01:00
|
|
|
} \
|
|
|
|
if(pc->seek) { \
|
2005-03-21 04:54:14 +01:00
|
|
|
dropBufferedAudio(); \
|
2004-10-26 04:13:13 +02:00
|
|
|
if(decodeSeek(pc,dc,cb,&decodeWaitedOn,&next) == 0) { \
|
2004-05-19 04:14:20 +02:00
|
|
|
doCrossFade = 0; \
|
|
|
|
nextChunk = -1; \
|
2004-05-20 05:44:33 +02:00
|
|
|
bbp = 0; \
|
2004-05-19 04:14:20 +02:00
|
|
|
} \
|
2004-02-24 00:41:20 +01:00
|
|
|
} \
|
|
|
|
if(pc->stop) { \
|
2005-03-05 15:01:13 +01:00
|
|
|
dropBufferedAudio(); \
|
2004-02-24 00:41:20 +01:00
|
|
|
quitDecode(pc,dc); \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void decodeStart(PlayerControl * pc, OutputBuffer * cb,
|
|
|
|
DecoderControl * dc)
|
|
|
|
{
|
2006-07-14 21:52:29 +02:00
|
|
|
int ret;
|
2008-01-01 11:09:56 +01:00
|
|
|
int close_instream = 1;
|
2006-07-14 21:52:29 +02:00
|
|
|
InputStream inStream;
|
2006-07-20 18:02:40 +02:00
|
|
|
InputPlugin *plugin = NULL;
|
2007-12-28 03:56:25 +01:00
|
|
|
char path_max_tmp[MPD_PATH_MAX];
|
2004-04-11 20:27:12 +02:00
|
|
|
|
2007-12-28 03:56:25 +01:00
|
|
|
/* not actually sure why we convert between latin/UTF8 for URLs */
|
|
|
|
if (isRemoteUrl(pc->utf8url)) {
|
|
|
|
if (!utf8_to_latin1(path_max_tmp, pc->utf8url)) {
|
|
|
|
dc->error = DECODE_ERROR_FILE;
|
2008-01-01 11:09:56 +01:00
|
|
|
goto stop_no_close;
|
2007-12-28 03:56:25 +01:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
rmp2amp_r(path_max_tmp,
|
|
|
|
utf8_to_fs_charset(path_max_tmp, pc->utf8url));
|
2004-05-31 13:42:46 +02:00
|
|
|
|
2004-06-06 21:41:03 +02:00
|
|
|
copyMpdTagToOutputBuffer(cb, NULL);
|
2004-05-31 13:42:46 +02:00
|
|
|
|
2006-07-30 12:31:41 +02:00
|
|
|
pathcpy_trunc(dc->utf8url, pc->utf8url);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2007-12-28 03:56:25 +01:00
|
|
|
if (openInputStream(&inStream, path_max_tmp) < 0) {
|
2004-05-18 15:13:55 +02:00
|
|
|
dc->error = DECODE_ERROR_FILE;
|
2008-01-01 11:09:56 +01:00
|
|
|
goto stop_no_close;
|
2006-07-14 21:52:29 +02:00
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-14 21:52:29 +02:00
|
|
|
dc->state = DECODE_STATE_START;
|
2004-05-19 04:14:20 +02:00
|
|
|
dc->start = 0;
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
while (!inputStreamAtEOF(&inStream) && bufferInputStream(&inStream) < 0
|
|
|
|
&& !dc->stop) {
|
2004-10-16 14:22:19 +02:00
|
|
|
/* sleep so we don't consume 100% of the cpu */
|
|
|
|
my_usleep(1000);
|
|
|
|
}
|
2004-05-19 04:14:20 +02:00
|
|
|
|
2006-12-23 20:57:28 +01:00
|
|
|
/* for http streams, seekable is determined in bufferInputStream */
|
|
|
|
dc->seekable = inStream.seekable;
|
2008-04-12 06:07:44 +02:00
|
|
|
|
2008-01-01 11:09:56 +01:00
|
|
|
if (dc->stop)
|
|
|
|
goto stop;
|
2004-05-31 13:42:46 +02:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
ret = DECODE_ERROR_UNKTYPE;
|
|
|
|
if (isRemoteUrl(dc->utf8url)) {
|
2006-03-16 07:52:46 +01:00
|
|
|
unsigned int next = 0;
|
2004-06-06 18:42:14 +02:00
|
|
|
cb->acceptMetadata = 1;
|
2006-03-16 07:52:46 +01:00
|
|
|
|
|
|
|
/* first we try mime types: */
|
2007-06-04 20:55:46 +02:00
|
|
|
while (ret && (plugin = getInputPluginFromMimeType(inStream.mime, next++))) {
|
2006-03-16 07:52:46 +01:00
|
|
|
if (!plugin->streamDecodeFunc)
|
|
|
|
continue;
|
|
|
|
if (!(plugin->streamTypes & INPUT_PLUGIN_STREAM_URL))
|
|
|
|
continue;
|
2006-07-20 18:02:40 +02:00
|
|
|
if (plugin->tryDecodeFunc
|
|
|
|
&& !plugin->tryDecodeFunc(&inStream))
|
2006-03-16 07:52:46 +01:00
|
|
|
continue;
|
|
|
|
ret = plugin->streamDecodeFunc(cb, dc, &inStream);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if that fails, try suffix matching the URL: */
|
2006-07-20 18:02:40 +02:00
|
|
|
if (plugin == NULL) {
|
2008-02-05 11:17:33 +01:00
|
|
|
const char *s = getSuffix(dc->utf8url);
|
2006-03-16 07:52:46 +01:00
|
|
|
next = 0;
|
2007-06-04 20:55:46 +02:00
|
|
|
while (ret && (plugin = getInputPluginFromSuffix(s, next++))) {
|
2006-03-16 07:52:46 +01:00
|
|
|
if (!plugin->streamDecodeFunc)
|
|
|
|
continue;
|
2006-07-20 18:02:40 +02:00
|
|
|
if (!(plugin->streamTypes &
|
|
|
|
INPUT_PLUGIN_STREAM_URL))
|
2006-03-16 07:52:46 +01:00
|
|
|
continue;
|
2006-07-20 18:02:40 +02:00
|
|
|
if (plugin->tryDecodeFunc &&
|
|
|
|
!plugin->tryDecodeFunc(&inStream))
|
2006-03-16 07:52:46 +01:00
|
|
|
continue;
|
2007-06-04 20:55:46 +02:00
|
|
|
ret = plugin->streamDecodeFunc(cb, dc, &inStream);
|
2006-03-16 07:52:46 +01:00
|
|
|
break;
|
|
|
|
}
|
2006-07-14 21:52:29 +02:00
|
|
|
}
|
2006-03-16 07:52:46 +01:00
|
|
|
/* fallback to mp3: */
|
2006-07-14 21:52:29 +02:00
|
|
|
/* this is needed for bastard streams that don't have a suffix
|
2006-07-20 18:02:40 +02:00
|
|
|
or set the mimeType */
|
|
|
|
if (plugin == NULL) {
|
2006-03-16 07:52:46 +01:00
|
|
|
/* we already know our mp3Plugin supports streams, no
|
|
|
|
* need to check for stream{Types,DecodeFunc} */
|
2006-07-14 21:52:29 +02:00
|
|
|
if ((plugin = getInputPluginFromName("mp3")))
|
2007-06-04 20:55:46 +02:00
|
|
|
ret = plugin->streamDecodeFunc(cb, dc, &inStream);
|
2006-07-14 21:52:29 +02:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
2006-03-16 07:52:46 +01:00
|
|
|
unsigned int next = 0;
|
2008-02-05 11:17:33 +01:00
|
|
|
const char *s = getSuffix(dc->utf8url);
|
2004-06-06 18:42:14 +02:00
|
|
|
cb->acceptMetadata = 0;
|
2006-03-16 07:52:46 +01:00
|
|
|
while (ret && (plugin = getInputPluginFromSuffix(s, next++))) {
|
2006-07-20 18:02:40 +02:00
|
|
|
if (!plugin->streamTypes & INPUT_PLUGIN_STREAM_FILE)
|
2006-03-16 07:52:46 +01:00
|
|
|
continue;
|
2007-06-25 15:22:51 +02:00
|
|
|
|
|
|
|
if (plugin->tryDecodeFunc &&
|
|
|
|
!plugin->tryDecodeFunc(&inStream))
|
2006-03-16 07:52:46 +01:00
|
|
|
continue;
|
2006-07-20 18:02:40 +02:00
|
|
|
|
2007-06-25 15:22:51 +02:00
|
|
|
if (plugin->fileDecodeFunc) {
|
2006-03-16 07:52:46 +01:00
|
|
|
closeInputStream(&inStream);
|
2008-01-01 11:09:56 +01:00
|
|
|
close_instream = 0;
|
2007-12-28 03:56:25 +01:00
|
|
|
ret = plugin->fileDecodeFunc(cb, dc,
|
|
|
|
path_max_tmp);
|
2007-06-25 15:22:51 +02:00
|
|
|
break;
|
|
|
|
} else if (plugin->streamDecodeFunc) {
|
|
|
|
ret = plugin->streamDecodeFunc(cb, dc, &inStream);
|
|
|
|
break;
|
2006-03-16 07:52:46 +01:00
|
|
|
}
|
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
}
|
2004-05-31 03:21:17 +02:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (ret < 0 || ret == DECODE_ERROR_UNKTYPE) {
|
2006-07-30 12:31:41 +02:00
|
|
|
pathcpy_trunc(pc->erroredUrl, dc->utf8url);
|
2008-01-01 11:09:56 +01:00
|
|
|
if (ret != DECODE_ERROR_UNKTYPE)
|
2006-07-20 18:02:40 +02:00
|
|
|
dc->error = DECODE_ERROR_FILE;
|
2008-01-01 11:09:56 +01:00
|
|
|
else
|
2006-07-14 21:52:29 +02:00
|
|
|
dc->error = DECODE_ERROR_UNKTYPE;
|
2004-05-18 15:13:55 +02:00
|
|
|
}
|
2008-01-01 11:09:56 +01:00
|
|
|
|
|
|
|
stop:
|
|
|
|
if (close_instream)
|
|
|
|
closeInputStream(&inStream);
|
|
|
|
stop_no_close:
|
|
|
|
dc->state = DECODE_STATE_STOP;
|
|
|
|
dc->stop = 0;
|
2004-05-18 15:13:55 +02:00
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static int decoderInit(PlayerControl * pc, OutputBuffer * cb,
|
|
|
|
DecoderControl * dc)
|
|
|
|
{
|
2007-06-05 00:29:55 +02:00
|
|
|
int pid;
|
|
|
|
|
|
|
|
pid = decode_pid;
|
|
|
|
if (pid > 0) {
|
|
|
|
kill(pid, SIGCONT);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-05-18 15:13:55 +02:00
|
|
|
blockSignals();
|
2005-03-09 04:13:33 +01:00
|
|
|
getPlayerData()->playerControl.decode_pid = 0;
|
|
|
|
decode_pid = fork();
|
2004-05-18 15:13:55 +02:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (decode_pid == 0) {
|
2004-05-18 15:13:55 +02:00
|
|
|
/* CHILD */
|
|
|
|
unblockSignals();
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
while (1) {
|
|
|
|
if (dc->cycleLogFiles) {
|
2006-08-01 06:18:53 +02:00
|
|
|
cycle_log_files();
|
2006-07-14 21:52:29 +02:00
|
|
|
dc->cycleLogFiles = 0;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else if (dc->start || dc->seek)
|
|
|
|
decodeStart(pc, cb, dc);
|
|
|
|
else if (dc->stop) {
|
2004-05-20 05:44:33 +02:00
|
|
|
dc->state = DECODE_STATE_STOP;
|
|
|
|
dc->stop = 0;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else
|
|
|
|
my_usleep(10000);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2004-04-03 01:34:16 +02:00
|
|
|
exit(EXIT_SUCCESS);
|
2004-02-24 00:41:20 +01:00
|
|
|
/* END OF CHILD */
|
2006-07-20 18:02:40 +02:00
|
|
|
} else if (decode_pid < 0) {
|
2004-04-11 20:27:12 +02:00
|
|
|
unblockSignals();
|
2006-07-30 12:31:41 +02:00
|
|
|
pathcpy_trunc(pc->erroredUrl, pc->utf8url);
|
2004-02-24 00:41:20 +01:00
|
|
|
pc->error = PLAYER_ERROR_SYSTEM;
|
|
|
|
return -1;
|
|
|
|
}
|
2005-11-16 15:43:04 +01:00
|
|
|
DEBUG("decoder PID: %d\n", decode_pid);
|
2005-03-09 04:13:33 +01:00
|
|
|
getPlayerData()->playerControl.decode_pid = decode_pid;
|
2004-04-11 20:27:12 +02:00
|
|
|
unblockSignals();
|
2004-02-24 00:41:20 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void handleMetadata(OutputBuffer * cb, PlayerControl * pc, int *previous,
|
|
|
|
int *currentChunkSent, MetadataChunk * currentChunk)
|
2004-06-07 14:11:57 +02:00
|
|
|
{
|
2006-07-20 18:02:40 +02:00
|
|
|
if (cb->begin != cb->end) {
|
2004-06-06 21:41:03 +02:00
|
|
|
int meta = cb->metaChunk[cb->begin];
|
2006-07-20 18:02:40 +02:00
|
|
|
if (meta != *previous) {
|
2004-06-09 18:58:33 +02:00
|
|
|
DEBUG("player: metadata change\n");
|
2006-07-20 18:02:40 +02:00
|
|
|
if (meta >= 0 && cb->metaChunkSet[meta]) {
|
2004-06-09 18:58:33 +02:00
|
|
|
DEBUG("player: new metadata from decoder!\n");
|
2006-07-20 18:02:40 +02:00
|
|
|
memcpy(currentChunk,
|
|
|
|
cb->metadataChunks + meta,
|
|
|
|
sizeof(MetadataChunk));
|
2004-06-07 14:11:57 +02:00
|
|
|
*currentChunkSent = 0;
|
2004-06-07 00:13:23 +02:00
|
|
|
cb->metaChunkSet[meta] = 0;
|
2004-06-06 21:41:03 +02:00
|
|
|
}
|
|
|
|
}
|
2004-06-07 14:11:57 +02:00
|
|
|
*previous = meta;
|
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
if (!(*currentChunkSent) && pc->metadataState ==
|
|
|
|
PLAYER_METADATA_STATE_WRITE) {
|
|
|
|
MpdTag *tag = NULL;
|
2004-10-25 22:11:35 +02:00
|
|
|
|
2004-06-07 14:11:57 +02:00
|
|
|
*currentChunkSent = 1;
|
2006-07-20 18:02:40 +02:00
|
|
|
|
|
|
|
if ((tag = metadataChunkToMpdTagDup(currentChunk))) {
|
2004-10-26 04:00:17 +02:00
|
|
|
sendMetadataToAudioDevice(tag);
|
2004-10-25 22:11:35 +02:00
|
|
|
freeMpdTag(tag);
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
memcpy(&(pc->metadataChunk), currentChunk,
|
|
|
|
sizeof(MetadataChunk));
|
2004-06-07 14:11:57 +02:00
|
|
|
pc->metadataState = PLAYER_METADATA_STATE_READ;
|
|
|
|
kill(getppid(), SIGUSR1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void advanceOutputBufferTo(OutputBuffer * cb, PlayerControl * pc,
|
|
|
|
int *previous, int *currentChunkSent,
|
|
|
|
MetadataChunk * currentChunk, int to)
|
2004-06-07 14:11:57 +02:00
|
|
|
{
|
2006-07-20 18:02:40 +02:00
|
|
|
while (cb->begin != to) {
|
|
|
|
handleMetadata(cb, pc, previous, currentChunkSent,
|
|
|
|
currentChunk);
|
2008-04-12 06:06:52 +02:00
|
|
|
if ((unsigned)cb->begin + 1 >= buffered_chunks) {
|
2004-06-07 14:11:57 +02:00
|
|
|
cb->begin = 0;
|
|
|
|
}
|
2007-02-11 21:36:26 +01:00
|
|
|
else cb->begin++;
|
2004-06-06 21:41:03 +02:00
|
|
|
}
|
2004-06-06 18:42:14 +02:00
|
|
|
}
|
|
|
|
|
2006-08-20 02:50:44 +02:00
|
|
|
static void decodeParent(PlayerControl * pc, DecoderControl * dc, OutputBuffer * cb)
|
2006-07-20 18:02:40 +02:00
|
|
|
{
|
2004-05-19 04:14:20 +02:00
|
|
|
int pause = 0;
|
|
|
|
int quit = 0;
|
2008-04-12 06:06:17 +02:00
|
|
|
unsigned int bbp = buffered_before_play;
|
2004-05-19 04:14:20 +02:00
|
|
|
int doCrossFade = 0;
|
2008-04-12 06:06:17 +02:00
|
|
|
unsigned int crossFadeChunks = 0;
|
|
|
|
unsigned int fadePosition;
|
2004-05-19 04:14:20 +02:00
|
|
|
int nextChunk = -1;
|
2008-04-12 06:06:17 +02:00
|
|
|
unsigned int test;
|
2006-07-20 18:02:40 +02:00
|
|
|
int decodeWaitedOn = 0;
|
2008-01-01 11:10:02 +01:00
|
|
|
static const char silence[CHUNK_SIZE];
|
2004-06-03 14:34:25 +02:00
|
|
|
double sizeToTime = 0.0;
|
2004-06-07 14:11:57 +02:00
|
|
|
int previousMetadataChunk = -1;
|
|
|
|
MetadataChunk currentMetadataChunk;
|
|
|
|
int currentChunkSent = 1;
|
2008-04-12 06:06:17 +02:00
|
|
|
unsigned int end;
|
2004-06-10 21:48:53 +02:00
|
|
|
int next = -1;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (waitOnDecode(pc, dc, cb, &decodeWaitedOn) < 0)
|
|
|
|
return;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
pc->elapsedTime = 0;
|
2004-05-19 04:14:20 +02:00
|
|
|
pc->state = PLAYER_STATE_PLAY;
|
|
|
|
pc->play = 0;
|
2006-07-20 18:02:40 +02:00
|
|
|
kill(getppid(), SIGUSR1);
|
2004-05-20 05:44:33 +02:00
|
|
|
|
2007-02-11 21:36:26 +01:00
|
|
|
while (decode_pid > 0 &&
|
2008-04-12 06:06:52 +02:00
|
|
|
(unsigned)(cb->end - cb->begin) < bbp &&
|
2006-07-20 18:02:40 +02:00
|
|
|
cb->end != buffered_chunks - 1 &&
|
|
|
|
dc->state != DECODE_STATE_STOP) {
|
2004-05-19 04:14:20 +02:00
|
|
|
processDecodeInput();
|
2005-03-10 11:47:06 +01:00
|
|
|
my_usleep(1000);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
while (!quit) {
|
2004-05-19 04:14:20 +02:00
|
|
|
processDecodeInput();
|
2006-07-20 18:02:40 +02:00
|
|
|
handleDecodeStart();
|
|
|
|
handleMetadata(cb, pc, &previousMetadataChunk,
|
|
|
|
¤tChunkSent, ¤tMetadataChunk);
|
|
|
|
if (dc->state == DECODE_STATE_STOP &&
|
|
|
|
pc->queueState == PLAYER_QUEUE_FULL &&
|
|
|
|
pc->queueLockState == PLAYER_QUEUE_UNLOCKED) {
|
2004-06-10 21:48:53 +02:00
|
|
|
next = cb->end;
|
2004-05-19 04:14:20 +02:00
|
|
|
dc->start = 1;
|
|
|
|
pc->queueState = PLAYER_QUEUE_DECODE;
|
2006-07-20 18:02:40 +02:00
|
|
|
kill(getppid(), SIGUSR1);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
if (next >= 0 && doCrossFade == 0 && !dc->start &&
|
|
|
|
dc->state != DECODE_STATE_START) {
|
2004-05-19 04:14:20 +02:00
|
|
|
nextChunk = -1;
|
2006-07-20 18:02:40 +02:00
|
|
|
if (isCurrentAudioFormat(&(cb->audioFormat))) {
|
2004-05-19 04:14:20 +02:00
|
|
|
doCrossFade = 1;
|
2006-07-20 18:02:40 +02:00
|
|
|
crossFadeChunks =
|
|
|
|
calculateCrossFadeChunks(pc,
|
|
|
|
&(cb->
|
|
|
|
audioFormat));
|
|
|
|
if (!crossFadeChunks
|
|
|
|
|| pc->crossFade >= dc->totalTime) {
|
2004-05-19 04:14:20 +02:00
|
|
|
doCrossFade = -1;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
} else
|
|
|
|
doCrossFade = -1;
|
2004-05-19 04:14:20 +02:00
|
|
|
}
|
2004-06-10 21:48:53 +02:00
|
|
|
|
2006-07-14 20:01:32 +02:00
|
|
|
/* copy these to local variables to prevent any potential
|
2006-07-20 18:02:40 +02:00
|
|
|
race conditions and weirdness */
|
2004-06-10 21:48:53 +02:00
|
|
|
end = cb->end;
|
|
|
|
|
2007-09-06 03:07:49 +02:00
|
|
|
if (pause) {
|
|
|
|
if (decode_pid)
|
|
|
|
kill(decode_pid, SIGSTOP);
|
|
|
|
kill(getpid(), SIGSTOP);
|
|
|
|
} else if (cb->begin != end && cb->begin != next) {
|
2006-07-20 18:02:40 +02:00
|
|
|
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) {
|
2004-05-19 04:14:20 +02:00
|
|
|
crossFadeChunks = fadePosition;
|
|
|
|
}
|
2004-06-10 21:48:53 +02:00
|
|
|
test = end;
|
2006-07-20 18:02:40 +02:00
|
|
|
if (end < cb->begin)
|
|
|
|
test += buffered_chunks;
|
|
|
|
nextChunk = cb->begin + crossFadeChunks;
|
2008-04-12 06:06:52 +02:00
|
|
|
if ((unsigned)nextChunk < test) {
|
|
|
|
if ((unsigned)nextChunk >= buffered_chunks) {
|
2006-07-20 18:02:40 +02:00
|
|
|
nextChunk -= buffered_chunks;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
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]
|
|
|
|
) {
|
2004-05-19 04:14:20 +02:00
|
|
|
cb->chunkSize[cb->begin]
|
2006-07-20 18:02:40 +02:00
|
|
|
= cb->chunkSize[nextChunk];
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
|
|
|
if (dc->state == DECODE_STATE_STOP) {
|
2004-05-19 04:14:20 +02:00
|
|
|
doCrossFade = -1;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else
|
|
|
|
continue;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
}
|
2004-05-30 15:33:13 +02:00
|
|
|
pc->elapsedTime = cb->times[cb->begin];
|
2004-05-19 04:14:20 +02:00
|
|
|
pc->bitRate = cb->bitRate[cb->begin];
|
2006-07-20 18:02:40 +02:00
|
|
|
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) {
|
2004-05-19 04:14:20 +02:00
|
|
|
quit = 1;
|
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
pc->totalPlayTime +=
|
|
|
|
sizeToTime * cb->chunkSize[cb->begin];
|
2008-04-12 06:06:52 +02:00
|
|
|
if ((unsigned)cb->begin + 1 >= buffered_chunks) {
|
2004-05-19 04:14:20 +02:00
|
|
|
cb->begin = 0;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else
|
|
|
|
cb->begin++;
|
notify the decoder instead of polling 100hz
When the decoder process is faster than the player process, all
decodedd buffers are full at some point in time. The decoder has to
wait for buffers to become free (finished playing). It used to do
this by polling the buffer status 100 times a second.
This generates a lot of unnecessary CPU wakeups. This patch adds a
way for the player process to notify the decoder process that it may
continue its work.
We could use pthread_cond for that, unfortunately inter-process
mutexes/conds are not supported by some kernels (Linux), so we cannot
use this light-weight method until mpd moves to using threads instead
of processes. The other method would be semaphores, which
historically are global resources with a unique name; this historic
API is cumbersome, and I wanted to avoid it.
I came up with a quite naive solution for now: I create an anonymous
pipe with pipe(), and the decoder process reads on that pipe. Until
the player process sends data on it as a signal, the decoder process
blocks.
This can be optimized in a number of ways:
- if the decoder process is still working (instead of waiting for
buffers), we could save the write() system call, since there is
nobody waiting for the notification.
[ew: I tried this using a counter in shared memory, didn't help]
- the pipe buffer will be full at some point, when the decoder thread
is too slow. For this reason, the writer side of the pipe is
non-blocking, and mpd can ignore the resulting EWOULDBLOCK.
- since we have shared memory, we could check whether somebody is
actually waiting without a context switch, and we could just not
write the notification byte.
[ew: tried same method/result as first point above]
- if there is already a notification in the pipe, we could also not
write another one.
[ew: tried same method/result as first/third points above]
- the decoder will only consume 64 bytes at a time. If the pipe
buffer is full, this will result in a lot of read() invocations.
This does not hurt badly, but on a heavily loaded system, this might
add a little bit more load. The preceding optimizations however
are able eliminate the this.
- finally, we should use another method for inter process
notifications - maybe kill() or just make mpd use threads, finally.
In spite of all these possibilities to optimize this code further,
this pipe notification trick is faster than the 100 Hz poll. On my
machine, it reduced the number of wakeups to less than 30%.
git-svn-id: https://svn.musicpd.org/mpd/trunk@7215 09075e82-0dd4-0310-85a5-a0d7c8717e4f
2008-03-26 11:38:54 +01:00
|
|
|
signalNotify(&cb->notify);
|
2007-02-11 21:36:26 +01:00
|
|
|
} else if (cb->begin != end && cb->begin == next) {
|
2006-07-20 18:02:40 +02:00
|
|
|
if (doCrossFade == 1 && nextChunk >= 0) {
|
|
|
|
nextChunk = cb->begin + crossFadeChunks;
|
2007-02-11 21:36:26 +01:00
|
|
|
test = end;
|
2006-07-20 18:02:40 +02:00
|
|
|
if (end < cb->begin)
|
|
|
|
test += buffered_chunks;
|
2008-04-12 06:06:52 +02:00
|
|
|
if ((unsigned)nextChunk < test) {
|
|
|
|
if ((unsigned)nextChunk >= buffered_chunks) {
|
2004-05-19 04:14:20 +02:00
|
|
|
nextChunk -= buffered_chunks;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
advanceOutputBufferTo(cb, pc,
|
|
|
|
&previousMetadataChunk,
|
|
|
|
¤tChunkSent,
|
|
|
|
¤tMetadataChunk,
|
|
|
|
nextChunk);
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
while (pc->queueState == PLAYER_QUEUE_DECODE ||
|
|
|
|
pc->queueLockState == PLAYER_QUEUE_LOCKED) {
|
2004-05-19 04:14:20 +02:00
|
|
|
processDecodeInput();
|
2004-05-20 02:21:58 +02:00
|
|
|
my_usleep(10000);
|
2004-05-19 04:14:20 +02:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
if (pc->queueState != PLAYER_QUEUE_PLAY) {
|
2004-02-24 00:41:20 +01:00
|
|
|
quit = 1;
|
|
|
|
break;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
2004-06-10 21:48:53 +02:00
|
|
|
next = -1;
|
2006-07-20 18:02:40 +02:00
|
|
|
if (waitOnDecode(pc, dc, cb, &decodeWaitedOn) <
|
|
|
|
0) {
|
2006-07-14 21:52:29 +02:00
|
|
|
return;
|
|
|
|
}
|
2004-05-19 04:14:20 +02:00
|
|
|
nextChunk = -1;
|
|
|
|
doCrossFade = 0;
|
|
|
|
crossFadeChunks = 0;
|
|
|
|
pc->queueState = PLAYER_QUEUE_EMPTY;
|
2006-07-20 18:02:40 +02:00
|
|
|
kill(getppid(), SIGUSR1);
|
2004-05-18 21:32:05 +02:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
} else if (decode_pid <= 0 ||
|
|
|
|
(dc->state == DECODE_STATE_STOP && !dc->start)) {
|
2004-05-19 04:14:20 +02:00
|
|
|
quit = 1;
|
|
|
|
break;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
2007-01-24 03:56:45 +01:00
|
|
|
/*DEBUG("waiting for decoded audio, play silence\n");*/
|
2006-07-20 18:02:40 +02:00
|
|
|
if (playAudio(silence, CHUNK_SIZE) < 0)
|
|
|
|
quit = 1;
|
2004-05-19 04:14:20 +02:00
|
|
|
}
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
quitDecode(pc, dc);
|
2004-05-19 04:14:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* decode w/ buffering
|
|
|
|
* this will fork another process
|
|
|
|
* child process does decoding
|
|
|
|
* parent process does playing audio
|
|
|
|
*/
|
2006-08-08 04:23:21 +02:00
|
|
|
void decode(void)
|
2006-07-20 18:02:40 +02:00
|
|
|
{
|
|
|
|
OutputBuffer *cb;
|
|
|
|
PlayerControl *pc;
|
|
|
|
DecoderControl *dc;
|
2004-05-19 04:14:20 +02:00
|
|
|
|
|
|
|
cb = &(getPlayerData()->buffer);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2004-06-09 18:58:33 +02:00
|
|
|
clearAllMetaChunkSets(cb);
|
2004-05-19 04:14:20 +02:00
|
|
|
cb->begin = 0;
|
|
|
|
cb->end = 0;
|
|
|
|
pc = &(getPlayerData()->playerControl);
|
|
|
|
dc = &(getPlayerData()->decoderControl);
|
|
|
|
dc->error = 0;
|
2006-07-14 21:52:29 +02:00
|
|
|
dc->seek = 0;
|
|
|
|
dc->stop = 0;
|
2004-05-20 05:44:33 +02:00
|
|
|
dc->start = 1;
|
2006-07-14 21:52:29 +02:00
|
|
|
|
2007-06-05 00:29:55 +02:00
|
|
|
if (decoderInit(pc, cb, dc) < 0)
|
|
|
|
return;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-14 21:52:29 +02:00
|
|
|
decodeParent(pc, dc, cb);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|