Update to 6762
[qball-mpd.git] / src / player.c
blob096503b1986ee4382bc1421f1e73a539a557d85e
1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "player.h"
20 #include "path.h"
21 #include "decode.h"
22 #include "command.h"
23 #include "interface.h"
24 #include "playlist.h"
25 #include "ls.h"
26 #include "listen.h"
27 #include "log.h"
28 #include "utils.h"
29 #include "directory.h"
30 #include "volume.h"
31 #include "playerData.h"
32 #include "permission.h"
33 #include "sig_handlers.h"
35 #include <stdio.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <sys/time.h>
40 #include <sys/wait.h>
41 #include <stdlib.h>
42 #include <signal.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <fcntl.h>
47 volatile int player_pid = 0;
49 void clearPlayerPid(void)
51 player_pid = 0;
54 static void resetPlayerMetadata(void)
56 PlayerControl *pc = &(getPlayerData()->playerControl);
58 if (pc->metadataState == PLAYER_METADATA_STATE_READ) {
59 pc->metadataState = PLAYER_METADATA_STATE_WRITE;
63 static void resetPlayer(void)
65 int pid;
67 clearPlayerPid();
68 getPlayerData()->playerControl.stop = 0;
69 getPlayerData()->playerControl.play = 0;
70 getPlayerData()->playerControl.pause = 0;
71 getPlayerData()->playerControl.lockQueue = 0;
72 getPlayerData()->playerControl.unlockQueue = 0;
73 getPlayerData()->playerControl.state = PLAYER_STATE_STOP;
74 getPlayerData()->playerControl.queueState = PLAYER_QUEUE_UNLOCKED;
75 getPlayerData()->playerControl.seek = 0;
76 getPlayerData()->playerControl.metadataState =
77 PLAYER_METADATA_STATE_WRITE;
78 pid = getPlayerData()->playerControl.decode_pid;
79 if (pid > 0)
80 kill(pid, SIGTERM);
81 getPlayerData()->playerControl.decode_pid = 0;
84 void player_sigChldHandler(int pid, int status)
86 if (player_pid == pid)
88 DEBUG("SIGCHLD caused by player process\n");
89 if (WIFSIGNALED(status) &&
90 WTERMSIG(status) != SIGTERM &&
91 WTERMSIG(status) != SIGINT)
93 ERROR("player process died from signal: %i\n",
94 WTERMSIG(status));
96 resetPlayer();
98 else if (pid == getPlayerData()->playerControl.decode_pid &&
99 player_pid <= 0)
101 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM)
103 ERROR("(caught by master parent) "
104 "decode process died from a "
105 "non-TERM signal: %i\n", WTERMSIG(status));
107 getPlayerData()->playerControl.decode_pid = 0;
111 int playerInit(void)
113 PlayerControl *pc = &(getPlayerData()->playerControl);
114 int pid;
116 pid = player_pid;
117 if (pid > 0) {
118 kill(pid, SIGCONT);
119 pc->wait = 0;
120 return 0;
123 blockSignals();
124 player_pid = fork();
125 if (player_pid==0)
127 clock_t start = clock();
129 unblockSignals();
131 setSigHandlersForDecoder();
133 closeAllListenSockets();
134 freeAllInterfaces();
135 finishPlaylist();
136 closeMp3Directory();
137 finishPermissions();
138 finishCommands();
139 finishVolume();
141 DEBUG("took %f to init player\n",
142 (float)(clock()-start)/CLOCKS_PER_SEC);
144 while (1) {
145 if (pc->play)
146 decode();
147 else if (pc->stop)
148 pc->stop = 0;
149 else if (pc->seek)
150 pc->seek = 0;
151 else if (pc->pause)
152 pc->pause = 0;
153 else if (pc->closeAudio) {
154 closeAudioDevice();
155 pc->closeAudio = 0;
156 kill(getppid(), SIGUSR1);
157 } else if (pc->lockQueue) {
158 pc->queueLockState = PLAYER_QUEUE_LOCKED;
159 pc->lockQueue = 0;
160 } else if (pc->unlockQueue) {
161 pc->queueLockState = PLAYER_QUEUE_UNLOCKED;
162 pc->unlockQueue = 0;
163 } else if (pc->cycleLogFiles) {
164 cycle_log_files();
165 pc->cycleLogFiles = 0;
166 } else
167 my_usleep(10000);
170 exit(EXIT_SUCCESS);
172 else if (player_pid < 0)
174 unblockSignals();
175 ERROR("player Problems fork()'ing\n");
176 player_pid = 0;
177 return -1;
180 unblockSignals();
182 return 0;
185 int playerWait(int fd)
187 PlayerControl *pc = &(getPlayerData()->playerControl);
188 int pid;
190 if (pc->wait)
191 return 0;
193 if (playerStop(fd) < 0)
194 return -1;
196 playerCloseAudio();
198 pid = player_pid;
199 if (pid > 0) {
200 pc->wait = 1;
201 kill(pid, SIGSTOP);
204 return 0;
207 int playerPlay(int fd, Song * song)
209 PlayerControl *pc = &(getPlayerData()->playerControl);
211 if (playerStop(fd) < 0)
212 return -1;
214 if (song->tag)
215 pc->fileTime = song->tag->time;
216 else
217 pc->fileTime = 0;
219 copyMpdTagToMetadataChunk(song->tag, &(pc->fileMetadataChunk));
221 pathcpy_trunc(pc->utf8url, getSongUrl(song));
223 pc->play = 1;
224 if (playerInit() < 0) {
225 pc->play = 0;
226 return -1;
229 resetPlayerMetadata();
230 while (player_pid > 0 && pc->play)
231 my_usleep(1000);
233 return 0;
236 int playerStop(int fd)
238 PlayerControl *pc = &(getPlayerData()->playerControl);
240 if (player_pid > 0 && pc->state != PLAYER_STATE_STOP) {
241 pc->stop = 1;
242 while (player_pid > 0 && pc->stop)
243 my_usleep(1000);
246 pc->queueState = PLAYER_QUEUE_BLANK;
247 playerQueueUnlock();
249 return 0;
252 void playerKill(void)
254 int pid;
256 pid = player_pid;
257 if (pid > 0) {
258 kill(pid, SIGCONT);
259 kill(pid, SIGTERM);
263 int playerPause(int fd)
265 PlayerControl *pc = &(getPlayerData()->playerControl);
267 if (player_pid > 0 && pc->state != PLAYER_STATE_STOP) {
268 pc->pause = 1;
269 while (player_pid > 0 && pc->pause)
270 my_usleep(1000);
273 return 0;
276 int playerSetPause(int fd, int pause)
278 PlayerControl *pc = &(getPlayerData()->playerControl);
280 if (player_pid <= 0)
281 return 0;
283 switch (pc->state) {
284 case PLAYER_STATE_PLAY:
285 if (pause)
286 playerPause(fd);
287 break;
288 case PLAYER_STATE_PAUSE:
289 if (!pause)
290 playerPause(fd);
291 break;
294 return 0;
297 int getPlayerElapsedTime(void)
299 return (int)(getPlayerData()->playerControl.elapsedTime + 0.5);
302 unsigned long getPlayerBitRate(void)
304 return getPlayerData()->playerControl.bitRate;
307 int getPlayerTotalTime(void)
309 return (int)(getPlayerData()->playerControl.totalTime + 0.5);
312 int getPlayerState(void)
314 return getPlayerData()->playerControl.state;
317 void clearPlayerError(void)
319 getPlayerData()->playerControl.error = 0;
322 int getPlayerError(void)
324 return getPlayerData()->playerControl.error;
327 char *getPlayerErrorStr(void)
329 static char *error;
330 int errorlen = MAXPATHLEN + 1024;
331 PlayerControl *pc = &(getPlayerData()->playerControl);
333 error = xrealloc(error, errorlen);
334 error[0] = '\0';
336 switch (pc->error) {
337 case PLAYER_ERROR_FILENOTFOUND:
338 snprintf(error, errorlen,
339 "file \"%s\" does not exist or is inaccessible",
340 pc->erroredUrl);
341 break;
342 case PLAYER_ERROR_FILE:
343 snprintf(error, errorlen, "problems decoding \"%s\"",
344 pc->erroredUrl);
345 break;
346 case PLAYER_ERROR_AUDIO:
347 snprintf(error, errorlen, "problems opening audio device");
348 break;
349 case PLAYER_ERROR_SYSTEM:
350 snprintf(error, errorlen, "system error occured");
351 break;
352 case PLAYER_ERROR_UNKTYPE:
353 snprintf(error, errorlen, "file type of \"%s\" is unknown",
354 pc->erroredUrl);
355 default:
356 break;
359 errorlen = strlen(error);
360 error = xrealloc(error, errorlen + 1);
362 if (errorlen)
363 return error;
365 return NULL;
368 void playerCloseAudio(void)
370 PlayerControl *pc = &(getPlayerData()->playerControl);
372 if (player_pid > 0) {
373 if (playerStop(STDERR_FILENO) < 0)
374 return;
375 pc->closeAudio = 1;
376 while (player_pid > 0 && pc->closeAudio)
377 my_usleep(1000);
381 int queueSong(Song * song)
383 PlayerControl *pc = &(getPlayerData()->playerControl);
385 if (pc->queueState == PLAYER_QUEUE_BLANK) {
386 pathcpy_trunc(pc->utf8url, getSongUrl(song));
388 if (song->tag)
389 pc->fileTime = song->tag->time;
390 else
391 pc->fileTime = 0;
393 copyMpdTagToMetadataChunk(song->tag, &(pc->fileMetadataChunk));
395 pc->queueState = PLAYER_QUEUE_FULL;
396 return 0;
399 return -1;
402 int getPlayerQueueState(void)
404 PlayerControl *pc = &(getPlayerData()->playerControl);
406 return pc->queueState;
409 void setQueueState(int queueState)
411 PlayerControl *pc = &(getPlayerData()->playerControl);
413 pc->queueState = queueState;
416 void playerQueueLock(void)
418 PlayerControl *pc = &(getPlayerData()->playerControl);
420 if (player_pid > 0 && pc->queueLockState == PLAYER_QUEUE_UNLOCKED) {
421 pc->lockQueue = 1;
422 while (player_pid > 0 && pc->lockQueue)
423 my_usleep(1000);
427 void playerQueueUnlock(void)
429 PlayerControl *pc = &(getPlayerData()->playerControl);
431 if (player_pid > 0 && pc->queueLockState == PLAYER_QUEUE_LOCKED) {
432 pc->unlockQueue = 1;
433 while (player_pid > 0 && pc->unlockQueue)
434 my_usleep(1000);
438 int playerSeek(int fd, Song * song, float time)
440 PlayerControl *pc = &(getPlayerData()->playerControl);
442 if (pc->state == PLAYER_STATE_STOP) {
443 commandError(fd, ACK_ERROR_PLAYER_SYNC,
444 "player not currently playing");
445 return -1;
448 if (strcmp(pc->utf8url, getSongUrl(song)) != 0) {
449 if (song->tag)
450 pc->fileTime = song->tag->time;
451 else
452 pc->fileTime = 0;
454 copyMpdTagToMetadataChunk(song->tag, &(pc->fileMetadataChunk));
456 pathcpy_trunc(pc->utf8url, getSongUrl(song));
459 if (pc->error == PLAYER_ERROR_NOERROR) {
460 resetPlayerMetadata();
461 pc->seekWhere = time;
462 pc->seek = 1;
463 while (player_pid > 0 && pc->seek)
464 my_usleep(1000);
467 return 0;
470 float getPlayerCrossFade(void)
472 PlayerControl *pc = &(getPlayerData()->playerControl);
474 return pc->crossFade;
477 void setPlayerCrossFade(float crossFadeInSeconds)
479 PlayerControl *pc;
480 if (crossFadeInSeconds < 0)
481 crossFadeInSeconds = 0;
483 pc = &(getPlayerData()->playerControl);
485 pc->crossFade = crossFadeInSeconds;
488 void setPlayerSoftwareVolume(int volume)
490 PlayerControl *pc;
491 volume = (volume > 1000) ? 1000 : (volume < 0 ? 0 : volume);
493 pc = &(getPlayerData()->playerControl);
495 pc->softwareVolume = volume;
498 double getPlayerTotalPlayTime(void)
500 PlayerControl *pc = &(getPlayerData()->playerControl);
502 return pc->totalPlayTime;
505 unsigned int getPlayerSampleRate(void)
507 PlayerControl *pc = &(getPlayerData()->playerControl);
509 return pc->sampleRate;
512 int getPlayerBits(void)
514 PlayerControl *pc = &(getPlayerData()->playerControl);
516 return pc->bits;
519 int getPlayerChannels(void)
521 PlayerControl *pc = &(getPlayerData()->playerControl);
523 return pc->channels;
526 void playerCycleLogFiles(void)
528 PlayerControl *pc = &(getPlayerData()->playerControl);
529 DecoderControl *dc = &(getPlayerData()->decoderControl);
531 pc->cycleLogFiles = 1;
532 dc->cycleLogFiles = 1;
535 /* this actually creates a dupe of the current metadata */
536 Song *playerCurrentDecodeSong(void)
538 static Song *song;
539 static MetadataChunk *prev;
540 Song *ret = NULL;
541 PlayerControl *pc = &(getPlayerData()->playerControl);
543 if (pc->metadataState == PLAYER_METADATA_STATE_READ) {
544 DEBUG("playerCurrentDecodeSong: caught new metadata!\n");
545 if (prev)
546 free(prev);
547 prev = xmalloc(sizeof(MetadataChunk));
548 memcpy(prev, &(pc->metadataChunk), sizeof(MetadataChunk));
549 if (song)
550 freeJustSong(song);
551 song = newNullSong();
552 song->url = xstrdup(pc->currentUrl);
553 song->tag = metadataChunkToMpdTagDup(prev);
554 ret = song;
555 resetPlayerMetadata();
558 return ret;