summaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
Diffstat (limited to 'misc')
-rw-r--r--misc/cdda_server.c513
1 files changed, 513 insertions, 0 deletions
diff --git a/misc/cdda_server.c b/misc/cdda_server.c
new file mode 100644
index 000000000..3ce9c126f
--- /dev/null
+++ b/misc/cdda_server.c
@@ -0,0 +1,513 @@
+/* cdda_server.c
+ *
+ * This is a TCP server that can be used with xine's cdda input plugin to
+ * play audio cds over the network.
+ *
+ * quick howto:
+ * - compile it:
+ * gcc -o cdda_server cdda_server.c
+ *
+ * - start the server:
+ * ./cdda_server /dev/cdrom 3000
+ *
+ * - start the client:
+ * xine cdda://server:3000/1
+ *
+ * to play the entire cd (using GUI's "CD" button) just change
+ * input.cdda_device to the server's mrl.
+ *
+ * 6 May 2003 - Miguel Freitas
+ * This feature was sponsored by 1Control
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#define QLEN 5 /* maximum connection queue length */
+#define _BUFSIZ 300
+
+static int msock;
+static int cdda_fd;
+static char *cdrom_device;
+
+/* CD-relevant defines and data structures */
+#define CD_SECONDS_PER_MINUTE 60
+#define CD_FRAMES_PER_SECOND 75
+#define CD_RAW_FRAME_SIZE 2352
+#define CD_LEADOUT_TRACK 0xAA
+
+
+#if defined (__linux__)
+
+#include <linux/cdrom.h>
+
+static int read_cdrom_toc_header(int fd, int *first_track, int *last_track) {
+
+ struct cdrom_tochdr tochdr;
+
+ /* fetch the table of contents */
+ if (ioctl(fd, CDROMREADTOCHDR, &tochdr) == -1) {
+ perror("CDROMREADTOCHDR");
+ return -1;
+ }
+
+ *first_track = tochdr.cdth_trk0;
+ *last_track = tochdr.cdth_trk1;
+ return 0;
+}
+
+static int read_cdrom_toc_entry(int fd, int track, int *track_mode,
+ int *first_frame_minute, int *first_frame_second, int *first_frame_frame ) {
+
+ struct cdrom_tocentry tocentry;
+
+ memset(&tocentry, 0, sizeof(tocentry));
+
+ tocentry.cdte_track = track;
+ tocentry.cdte_format = CDROM_MSF;
+ if (ioctl(fd, CDROMREADTOCENTRY, &tocentry) == -1) {
+ perror("CDROMREADTOCENTRY");
+ return -1;
+ }
+
+ *track_mode = (tocentry.cdte_ctrl & 0x04) ? 1 : 0;
+ *first_frame_minute = tocentry.cdte_addr.msf.minute;
+ *first_frame_second = tocentry.cdte_addr.msf.second;
+ *first_frame_frame = tocentry.cdte_addr.msf.frame;
+
+ return 0;
+}
+
+static int read_cdrom_frames(int fd, int frame, int num_frames,
+ unsigned char *data) {
+
+ struct cdrom_msf msf;
+
+ while( num_frames ) {
+ /* read from starting frame... */
+ msf.cdmsf_min0 = frame / CD_SECONDS_PER_MINUTE / CD_FRAMES_PER_SECOND;
+ msf.cdmsf_sec0 = (frame / CD_FRAMES_PER_SECOND) % CD_SECONDS_PER_MINUTE;
+ msf.cdmsf_frame0 = frame % CD_FRAMES_PER_SECOND;
+
+ /* read until ending track (starting frame + 1)... */
+ msf.cdmsf_min1 = (frame + 1) / CD_SECONDS_PER_MINUTE / CD_FRAMES_PER_SECOND;
+ msf.cdmsf_sec1 = ((frame + 1) / CD_FRAMES_PER_SECOND) % CD_SECONDS_PER_MINUTE;
+ msf.cdmsf_frame1 = (frame + 1) % CD_FRAMES_PER_SECOND;
+
+ /* MSF structure is the input to the ioctl */
+ memcpy(data, &msf, sizeof(msf));
+
+ /* read a frame */
+ if(ioctl(fd, CDROMREADRAW, data, data) < 0) {
+ perror("CDROMREADRAW");
+ return -1;
+ }
+ data += CD_RAW_FRAME_SIZE;
+ frame++;
+ num_frames--;
+ }
+ return 0;
+}
+
+#endif
+
+/* network functions */
+
+static int sock_has_data(int socket){
+
+ fd_set readfds;
+ fd_set writefds;
+ fd_set exceptfds;
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ FD_SET(socket, &readfds);
+
+ return (select(socket+1, &readfds, &writefds, &exceptfds, &tv) > 0);
+}
+
+static int sock_check_opened(int socket) {
+ fd_set readfds, writefds, exceptfds;
+ int retval;
+ struct timeval timeout;
+
+ for(;;) {
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ FD_SET(socket, &exceptfds);
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ retval = select(socket + 1, &readfds, &writefds, &exceptfds, &timeout);
+
+ if(retval == -1 && (errno != EAGAIN && errno != EINTR))
+ return 0;
+
+ if (retval != -1)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * read binary data from socket
+ */
+static int sock_data_read (int socket, char *buf, int nlen) {
+ int n, num_bytes;
+
+ if((socket < 0) || (buf == NULL))
+ return -1;
+
+ if(!sock_check_opened(socket))
+ return -1;
+
+ num_bytes = 0;
+
+ while (num_bytes < nlen) {
+
+ n = read (socket, &buf[num_bytes], nlen - num_bytes);
+
+ /* read errors */
+ if (n < 0) {
+ if(errno == EAGAIN) {
+ fd_set rset;
+ struct timeval timeout;
+
+ FD_ZERO (&rset);
+ FD_SET (socket, &rset);
+
+ timeout.tv_sec = 30;
+ timeout.tv_usec = 0;
+
+ if (select (socket+1, &rset, NULL, NULL, &timeout) <= 0) {
+ printf ("network: timeout on read\n");
+ return 0;
+ }
+ continue;
+ }
+ printf ("network: read error %d\n", errno);
+ return 0;
+ }
+
+ num_bytes += n;
+
+ /* end of stream */
+ if (!n) break;
+ }
+
+ return num_bytes;
+}
+
+/*
+ * read a line (\n-terminated) from socket
+ */
+static int sock_string_read(int socket, char *buf, int len) {
+ char *pbuf;
+ int r, rr;
+ void *nl;
+
+ if((socket < 0) || (buf == NULL))
+ return -1;
+
+ if(!sock_check_opened(socket))
+ return -1;
+
+ if (--len < 1)
+ return(-1);
+
+ pbuf = buf;
+
+ do {
+
+ if((r = recv(socket, pbuf, len, MSG_PEEK)) <= 0)
+ return -1;
+
+ if((nl = memchr(pbuf, '\n', r)) != NULL)
+ r = ((char *) nl) - pbuf + 1;
+
+ if((rr = read(socket, pbuf, r)) < 0)
+ return -1;
+
+ pbuf += rr;
+ len -= rr;
+
+ } while((nl == NULL) && len);
+
+ if (pbuf > buf && *(pbuf-1) == '\n'){
+ *(pbuf-1) = '\0';
+ }
+ *pbuf = '\0';
+ return (pbuf - buf);
+}
+
+/*
+ * Write to socket.
+ */
+static int sock_data_write(int socket, char *buf, int len) {
+ ssize_t size;
+ int wlen = 0;
+
+ if((socket < 0) || (buf == NULL))
+ return -1;
+
+ if(!sock_check_opened(socket))
+ return -1;
+
+ while(len) {
+ size = write(socket, buf, len);
+
+ if(size <= 0) {
+ printf("error writing to socket %d\n",socket);
+ return -1;
+ }
+
+ len -= size;
+ wlen += size;
+ buf += size;
+ }
+
+ return wlen;
+}
+
+int sock_string_write(int socket, char *msg, ...) {
+ char buf[_BUFSIZ];
+ va_list args;
+
+ va_start(args, msg);
+ vsnprintf(buf, _BUFSIZ, msg, args);
+ va_end(args);
+
+ /* Each line sent is '\n' terminated */
+ if((buf[strlen(buf)] == '\0') && (buf[strlen(buf) - 1] != '\n'))
+ sprintf(buf, "%s%c", buf, '\n');
+
+ return sock_data_write(socket, buf, strlen(buf));
+}
+
+
+#define CMD_CDDA_READ "cdda_read"
+#define CMD_CDDA_TOCHDR "cdda_tochdr"
+#define CMD_CDDA_TOCENTRY "cdda_tocentry"
+#define CMD_CDDA_OPEN "cdda_open"
+
+static int process_commands( int socket )
+{
+ char cmd[_BUFSIZ];
+ int start_frame, num_frames, i;
+ int ret, n;
+
+ while( sock_has_data(socket) )
+ {
+ if( sock_string_read(socket, cmd, _BUFSIZ) <= 0 )
+ return -1;
+
+ if( !strncmp(cmd, CMD_CDDA_OPEN, strlen(CMD_CDDA_OPEN)) ) {
+
+ if( cdda_fd != -1 )
+ close(cdda_fd);
+
+ cdda_fd = open ( cdrom_device, O_RDONLY);
+ if( cdda_fd == -1 )
+ {
+ printf( "argh ! couldn't open CD (%s)\n", cdrom_device );
+ if( sock_string_write(socket,"-1 0") < 0 )
+ return -1;
+ }
+ else {
+ if( sock_string_write(socket,"0 0") < 0 )
+ return -1;
+ }
+ continue;
+ }
+
+ if( cdda_fd != -1 ) {
+
+ if( !strncmp(cmd, CMD_CDDA_READ, strlen(CMD_CDDA_READ)) ) {
+ char *buf;
+
+ sscanf(cmd,"%*s %d %d", &start_frame, &num_frames);
+
+ n = num_frames * CD_RAW_FRAME_SIZE;
+ buf = malloc( n );
+ if( !buf )
+ {
+ printf("fatal error: could not allocate memory\n");
+ exit(1);
+ }
+
+ ret = read_cdrom_frames(cdda_fd, start_frame, num_frames, buf );
+
+ if( sock_string_write(socket,"%d %d", ret, n) < 0 )
+ return -1;
+
+ if( sock_data_write(socket,buf,n) < 0 )
+ return -1;
+
+ free(buf);
+ continue;
+ }
+
+ if( !strncmp(cmd, CMD_CDDA_TOCHDR, strlen(CMD_CDDA_TOCHDR)) ) {
+ int first_track, last_track;
+
+ ret = read_cdrom_toc_header(cdda_fd, &first_track, &last_track);
+
+ if( sock_string_write(socket,"%d 0 %d %d", ret, first_track, last_track) < 0 )
+ return -1;
+
+ continue;
+ }
+
+ if( !strncmp(cmd, CMD_CDDA_TOCENTRY, strlen(CMD_CDDA_TOCENTRY)) ) {
+ int track_mode, first_frame_minute, first_frame_second, first_frame_frame;
+
+ sscanf(cmd,"%*s %d", &i);
+
+ ret = read_cdrom_toc_entry(cdda_fd, i, &track_mode,
+ &first_frame_minute, &first_frame_second, &first_frame_frame );
+
+ if( sock_string_write(socket,"%d 0 %d %d %d %d", ret,
+ track_mode, first_frame_minute, first_frame_second, first_frame_frame) < 0 )
+ return -1;
+
+ continue;
+ }
+
+ } else {
+
+ /* no device open. return error */
+ if( sock_string_write(socket,"-1 0") < 0 )
+ return -1;
+
+ }
+
+ }
+ return 0;
+}
+
+static void server_loop()
+{
+ struct sockaddr_in fsin; /* the from address of a client */
+ int alen; /* from-address length */
+ int fd, nfds;
+ fd_set rfds; /* read file descriptor set */
+ fd_set afds; /* active file descriptor set */
+
+ /* SIGPIPE when connection closed during write call */
+ signal( SIGPIPE, SIG_IGN );
+
+ nfds = getdtablesize();
+
+ FD_ZERO(&afds);
+
+ FD_SET(msock, &afds);
+
+ while (1)
+ {
+ memcpy( &rfds, &afds, sizeof(rfds) );
+
+ if (select(nfds, &rfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0) < 0)
+ continue; /* erro?? */
+
+ if (FD_ISSET(msock, &rfds))
+ {
+ int ssock;
+ alen = sizeof(fsin);
+
+ ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
+ if (ssock < 0)
+ continue; /* erro?? */
+
+ FD_SET(ssock, &afds);
+ printf("new connection socket %d\n", ssock);
+ }
+
+ for (fd=0; fd<nfds; ++fd)
+ {
+ if (fd != msock && FD_ISSET(fd, &rfds))
+ if (process_commands(fd) < 0)
+ {
+ printf("closing socket %d\n", fd);
+ close(fd);
+ FD_CLR(fd, &afds);
+
+ if( cdda_fd != -1 ) {
+ close(cdda_fd);
+ cdda_fd = -1;
+ }
+ }
+ }
+ }
+}
+
+
+int main( int argc, char *argv[] )
+{
+ unsigned int port;
+ int ret;
+ struct sockaddr_in servAddr;
+
+ /* Print version number */
+ printf( "CDDA TCP Server\n" );
+
+ /* Check for 2 arguments */
+ if( argc != 3 )
+ {
+ printf( "usage: %s <device> <server_port>\n", argv[0] );
+ return -1;
+ }
+
+ port = atoi( argv[2] );
+
+ cdda_fd = -1;
+ cdrom_device = argv[1];
+
+ msock = socket(PF_INET, SOCK_STREAM, 0);
+ if( msock < 0 )
+ {
+ printf("error opening master socket.\n");
+ return 0;
+ }
+ servAddr.sin_family = AF_INET;
+ servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ servAddr.sin_port = htons(port);
+
+ if(bind(msock, (struct sockaddr *) &servAddr, sizeof(servAddr))<0)
+ {
+ printf("bind port %d error\n", port);
+ return 0;
+ }
+
+ listen(msock,QLEN);
+
+ printf("listening on port %d...\n", port);
+
+ server_loop();
+
+ close(msock);
+
+ return ret;
+}
+