1 /* remote.c by Richard van Paasen <rvpaasen@dds.nl> */
3 /********************************************************************
5 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
6 * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
7 * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
8 * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
10 * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001 *
11 * by Kenneth C. Arnold <ogg@arnoldnet.net> AND OTHER CONTRIBUTORS *
12 * http://www.xiph.org/ *
14 ********************************************************************
18 ********************************************************************/
29 #include <semaphore.h>
32 #include <sys/select.h>
38 /* Maximum size of the input buffer */
40 /* Undefine logfile if you don't want it */
41 //#define LOGFILE "/tmp/ogg123.log"
43 /* The play function in ogg123.c */
44 extern void play (char *source_string
);
45 extern ogg123_options_t options
;
46 extern void set_seek_opt(ogg123_options_t
*ogg123_opts
, char *buf
);
49 typedef enum { PLAY
, STOP
, PAUSE
, NEXT
, QUIT
} Status
;
50 static Status status
= STOP
;
52 /* Threading is introduced to reduce the
53 amount of processor time that ogg123
54 will take if it is in idle state */
56 /* Thread control locks */
57 static pthread_mutex_t main_lock
;
58 static sem_t sem_command
;
59 static sem_t sem_processed
;
60 static pthread_mutex_t output_lock
;
63 void send_log(const char* fmt
, ...) {
67 pthread_mutex_lock (&output_lock
);
68 fp
=fopen(LOGFILE
,"a");
70 vfprintf(fp
, fmt
, ap
);
74 pthread_mutex_unlock (&output_lock
);
81 static void send_msg(const char* fmt
, ...) {
84 pthread_mutex_lock (&output_lock
);
87 vfprintf(stdout
, fmt
, ap
);
89 fprintf(stdout
, "\n");
90 pthread_mutex_unlock (&output_lock
);
94 static void send_err(const char* fmt
, ...) {
96 pthread_mutex_lock (&output_lock
);
99 vfprintf(stderr
, fmt
, ap
);
101 fprintf(stderr
, "\n");
102 pthread_mutex_unlock (&output_lock
);
106 static Status
getstatus() {
111 static void setstatus(Status s
) {
116 static void invertpause() {
121 else if (status
==PAUSE
) {
127 static void * remotethread(void * arg
) {
144 send_log("Waiting for input: ...");
149 select (1, &fd
, NULL
, NULL
, NULL
);
152 fgets(buf
, MAXBUF
, stdin
);
153 buf
[strlen(buf
)-1] = 0;
156 pthread_mutex_lock (&main_lock
);
158 send_log("Input: %s", buf
);
161 if (!strncasecmp(buf
,"l",1)) {
162 /* prepare to load */
163 if ((b
=strchr(buf
,' ')) != NULL
) {
164 /* Prepare to load a new song */
165 strcpy((char*)arg
, b
+1);
169 /* Invalid load command */
174 if (!strncasecmp(buf
,"p",1)) {
175 /* Prepare to (un)pause */
179 if (!strncasecmp(buf
,"j",1)) {
180 /* Prepare to seek */
181 if ((b
=strchr(buf
,' ')) != NULL
) {
182 set_seek_opt(&options
, b
+1);
187 if (!strncasecmp(buf
,"s",1)) {
188 /* Prepare to stop */
192 if (!strncasecmp(buf
,"r",1)) {
193 /* Prepare to reload */
197 if (!strncasecmp(buf
,"h",1)) {
199 send_msg("H +----------------------------------------------------+");
200 send_msg("H | Ogg123 remote interface |");
201 send_msg("H |----------------------------------------------------|");
202 send_msg("H | Load <file> - load a file and starts playing |");
203 send_msg("H | Pause - pause or unpause playing |");
204 send_msg("H | Jump [+|-]<f> - jump <f> seconds forth or back |");
205 send_msg("H | Stop - stop playing |");
206 send_msg("H | Reload - reload last song |");
207 send_msg("H | Quit - quit ogg123 |");
208 send_msg("H |----------------------------------------------------|");
209 send_msg("H | refer to README.remote for documentation |");
210 send_msg("H +----------------------------------------------------+");
214 if (!strncasecmp(buf
,"q",1)) {
215 /* Prepare to quit */
220 /* Unknown input received */
226 pthread_mutex_unlock (&main_lock
);
230 /* Send the error and unlock */
231 send_err("E Unknown command '%s'", buf
);
232 send_log("Unknown command '%s'", buf
);
234 pthread_mutex_unlock (&main_lock
);
237 /* Signal the main thread */
238 sem_post(&sem_command
);
240 pthread_mutex_unlock (&main_lock
);
241 /* Wait until the change has been noticed */
242 sem_wait(&sem_processed
);
250 void remote_mainloop(void) {
255 char fname
[MAXBUF
+1];
257 /* Need to output line by line! */
260 /* Send a greeting */
261 send_msg("R ogg123 from " PACKAGE
" " VERSION
);
263 /* Initialize the thread controlling variables */
264 pthread_mutex_init(&main_lock
, NULL
);
265 sem_init(&sem_command
, 0, 0);
266 sem_init(&sem_processed
, 0, 0);
268 /* Start the thread */
269 r
= pthread_create(&th
, NULL
, remotethread
, (void*)fname
);
271 send_err("E Could not create a thread (code %d)", r
);
277 /* The thread may already have processed some input,
278 get the current status
280 pthread_mutex_lock(&main_lock
);
282 pthread_mutex_unlock(&main_lock
);
286 /* wait for a new command */
289 /* Wait until a new status is available,
290 This puts the main tread asleep and
294 sem_wait(&sem_command
);
296 pthread_mutex_lock(&main_lock
);
298 pthread_mutex_unlock(&main_lock
);
301 send_log("Status: %d", s
);
305 /* The status is to play a new song. Set
306 the status to PLAY and signal the thread
307 that the status has been processed.
310 send_msg("I %s", fname
);
311 send_msg("S 0.0 0 00000 xxxxxx 0 0 0 0 0 0 0 0");
313 pthread_mutex_lock(&main_lock
);
316 send_log("mainloop s=%d", s
);
317 sem_post(&sem_processed
);
319 send_log("mainloop s=%d", s
);
320 pthread_mutex_unlock(&main_lock
);
322 /* Start the player. The player calls the playloop
323 frequently to check for a new status (e.g. NEXT,
327 pthread_mutex_lock(&main_lock
);
329 pthread_mutex_unlock(&main_lock
);
330 send_log("mainloop s=%d", s
);
333 /* Retrieve the new status */
334 pthread_mutex_lock(&main_lock
);
336 pthread_mutex_unlock(&main_lock
);
338 /* don't know why this was here, sending "play stoped" on NEXT wasn't good idea... */
341 /* Send "play stopped" */
346 /* Send "play stopped at eof" */
347 // send_msg("P 0 EOF");
348 // send_log("P 0 EOF");
354 /* Irrelevent status, notice the thread that
355 it has been processed.
358 sem_post(&sem_processed
);
366 /* Cleanup the semaphores */
367 sem_destroy(&sem_command
);
368 sem_destroy(&sem_processed
);
373 int remote_playloop(void) {
377 /* Check the status. If the player should pause,
378 then signal that the command has been processed
379 and wait for a new status. A new status will end
380 the player and return control to the main loop.
381 The main loop will signal that the new command
385 pthread_mutex_lock (&main_lock
);
387 pthread_mutex_unlock (&main_lock
);
389 send_log("playloop entry s=%d", s
);
393 /* Send "pause on" */
398 sem_post(&sem_processed
);
399 sem_wait(&sem_command
);
400 pthread_mutex_lock (&main_lock
);
402 pthread_mutex_unlock (&main_lock
);
405 /* Send "pause off" */
409 /* Send stop msg to the frontend */
410 /* this probably should be done after the audio buffer is flushed and no audio is actually playing, but don't know how */
411 if ((s
== STOP
) || (s
== QUIT
)) send_msg("P 0");
413 send_log("playloop exit s=%d", s
);
415 return ((s
== NEXT
) || (s
== STOP
) || (s
== QUIT
));
418 void remote_time(double current
, double total
) {
420 /* Send the frame (not implemented yet) and the time */
421 send_msg("F 0 0 %.2f %.2f", current
, (total
-current
));