Initial revision 6759
[qball-mpd.git] / src / .svn / text-base / player.c.svn-base
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
4  *
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.
9  *
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
17  */
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;
60         }
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) 
87         {
88                 DEBUG("SIGCHLD caused by player process\n");
89                 if (WIFSIGNALED(status) && 
90                     WTERMSIG(status) != SIGTERM &&
91                     WTERMSIG(status) != SIGINT) 
92                 {
93                         ERROR("player process died from signal: %i\n",
94                               WTERMSIG(status));
95                 }
96                 resetPlayer();
97         } 
98         else if (pid == getPlayerData()->playerControl.decode_pid &&
99                  player_pid <= 0) 
100         {
101                 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) 
102                 {
103                         ERROR("(caught by master parent) "
104                               "decode process died from a "
105                               "non-TERM signal: %i\n", WTERMSIG(status));
106                 }
107                 getPlayerData()->playerControl.decode_pid = 0;
108         }
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;
121         }
123         blockSignals();
124         player_pid = fork();
125         if (player_pid==0)
126         {
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);
168                 }
170                 exit(EXIT_SUCCESS);
171         } 
172         else if (player_pid < 0) 
173         {
174                 unblockSignals();
175                 ERROR("player Problems fork()'ing\n");
176                 player_pid = 0;
177                 return -1;
178         }
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);
202         }
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;
227         }
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);
244         }
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);
260         }
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);
271         }
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;
292         }
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;
357         }
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);
378         }
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;
397         }
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);
424         }
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);
435         }
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;
446         }
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));
457         }
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);
465         }
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();
556         }
558         return ret;