summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/input/input_cda.c488
1 files changed, 482 insertions, 6 deletions
diff --git a/src/input/input_cda.c b/src/input/input_cda.c
index 4dd7914bd..8d24a0275 100644
--- a/src/input/input_cda.c
+++ b/src/input/input_cda.c
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: input_cda.c,v 1.1 2001/12/06 23:53:20 f1rmb Exp $
+ * $Id: input_cda.c,v 1.2 2001/12/08 03:01:40 f1rmb Exp $
*/
#ifdef HAVE_CONFIG_H
@@ -26,6 +26,15 @@
#include <stdio.h>
#include <stdlib.h>
+
+#include <netdb.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <pwd.h>
+#include <sys/types.h>
+
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
@@ -45,7 +54,6 @@
#if ! defined (HAVE_LINUX_CDROM_H) && ! defined (HAVE_SYS_CDIO_H)
#error "you need to add cdrom / CDA support for your platform to input_cda and configure.in"
#endif
-
#include "xine_internal.h"
#include "xineutils.h"
#include "input_plugin.h"
@@ -57,6 +65,9 @@
#define CDROM "/dev/cdaudio"
+#define CDDB_SERVER "freedb.freedb.org"
+#define CDDB_PORT 8880
+
/* for type */
#define CDAUDIO 1
#define CDDATA 2
@@ -76,6 +87,7 @@ typedef struct {
int length;
int start;
int track;
+ char *title;
} trackinfo_t;
typedef struct {
@@ -87,6 +99,11 @@ typedef struct {
int num_tracks;
int first_track;
int length;
+ unsigned long disc_id;
+ char *title;
+ char *category;
+ char *cdiscid;
+ char ui_title[256];
trackinfo_t *track;
} cdainfo_t;
@@ -100,6 +117,12 @@ typedef struct {
uint32_t speed;
char *mrl;
+
+ struct {
+ char *server;
+ int port;
+ int fd;
+ } cddb;
cdainfo_t *cda;
@@ -117,6 +140,385 @@ static void _cda_stop_cd(cdainfo_t *);
*/
/*
+ * Return user name.
+ */
+static char *_cda_get_username_safe(void) {
+ char *un;
+ struct passwd *pw;
+
+ pw = getpwuid(geteuid());
+
+ un = strdup(((pw && pw->pw_name) ? strdup(pw->pw_name) : "unknown"));
+
+ return un;
+}
+
+/*
+ * Return host name.
+ */
+static char *_cda_get_hostname_safe(void) {
+ char *hn;
+ char buf[2048];
+
+ memset(&buf, 0, sizeof(buf));
+ if(gethostname(&buf[0], sizeof(buf)) == 0)
+ hn = strdup(buf);
+ else
+ hn = strdup("unknown");
+
+ return hn;
+}
+
+/*
+ * ************* CDDB *********************
+ */
+/*
+ * Small sighandler ;-)
+ */
+static void die(int signal) {
+ exit(signal);
+}
+
+/*
+ * Read from socket, fill char *s, return size length.
+ */
+static int _cda_cddb_socket_read(char *s, int size, int socket) {
+ int i = 0, r;
+ char c;
+
+ alarm(20);
+ signal(SIGALRM, die);
+
+ while((r=recv(socket, &c, 1, 0)) != 0) {
+ if(c == '\r' || c == '\n')
+ break;
+ if(i > size)
+ break;
+ s[i] = c;
+ i++;
+ }
+ s[i] = '\n';
+ s[i+1] = 0;
+ recv(socket, &c, 1, 0);
+
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ s[i] = 0;
+ return r;
+}
+
+/*
+ * Send a command to socket
+ */
+static int _cda_cddb_send_command(cda_input_plugin_t *this, char *cmd) {
+
+ if((this == NULL) || (this->cddb.fd < 0) || (cmd == NULL))
+ return -1;
+
+ return (send(this->cddb.fd, cmd, strlen(cmd), 0));
+}
+
+/*
+ * Handle return code od a command result.
+ */
+static int _cda_cddb_handle_code(char *buf) {
+ int rcode, fdig, sdig, tdig;
+ int err = -1;
+
+ if(sscanf(buf, "%d", &rcode) == 1) {
+
+ fdig = rcode / 100;
+ sdig = (rcode - (fdig * 100)) / 10;
+ tdig = (rcode - (fdig * 100) - (sdig * 10));
+
+ /*
+ printf(" %d--\n", fdig);
+ printf(" -%d-\n", sdig);
+ printf(" --%d\n", tdig);
+ */
+ switch(fdig) {
+ case 1:
+ // printf("Informative message\n");
+ err = 0;
+ break;
+ case 2:
+ // printf("Command OK\n");
+ err = 0;
+ break;
+ case 3:
+ // printf("Command OK so far, continue\n");
+ err = 0;
+ break;
+ case 4:
+ // printf("Command OK, but cannot be performed for some specified reasons\n");
+ err = -1;
+ break;
+ case 5:
+ // printf("Command unimplemented, incorrect, or program error\n");
+ err = -1;
+ break;
+ default:
+ // printf("Unhandled case %d\n", fdig);
+ err = -1;
+ break;
+ }
+
+ switch(sdig) {
+ case 0:
+ // printf("Ready for further commands\n");
+ err = 0;
+ break;
+ case 1:
+ // printf("More server-to-client output follows (until terminating marker)\n");
+ err = 0;
+ break;
+ case 2:
+ // printf("More client-to-server input follows (until terminating marker)\n");
+ err = 0;
+ break;
+ case 3:
+ // printf("Connection will close\n");
+ err = -1;
+ break;
+ default:
+ // printf("Unhandled case %d\n", sdig);
+ err = -1;
+ break;
+ }
+
+ if(err >= 0)
+ err = rcode;
+ }
+
+ return err;
+}
+
+/*
+ * Try to talk with CDDB server (to retrieve disc/tracks titles).
+ */
+static void _cda_cddb_retrieve(cda_input_plugin_t *this) {
+ char buffer[2048];
+ char *username, *hostname;
+ int err, i;
+
+ if((this == NULL) || (this->cddb.fd < 0))
+ return;
+
+ username = _cda_get_username_safe();
+ hostname = _cda_get_hostname_safe();
+
+ memset(&buffer, 0, sizeof(buffer));
+
+ /* Get welcome message */
+ if(_cda_cddb_socket_read(&buffer[0], 2047, this->cddb.fd)) {
+ if((err = _cda_cddb_handle_code(buffer)) >= 0) {
+ /* send hello */
+ memset(&buffer, 0, sizeof(buffer));
+ sprintf(buffer, "cddb hello %s %s xine %s\n", username, hostname, VERSION);
+ if((err = _cda_cddb_send_command(this, buffer)) > 0) {
+ /* Get answer from hello */
+ memset(&buffer, 0, sizeof(buffer));
+ if(_cda_cddb_socket_read(&buffer[0], 2047, this->cddb.fd)) {
+ /* Parse returned code */
+ if((err = _cda_cddb_handle_code(buffer)) >= 0) {
+ /* We are logged, query disc */
+ memset(&buffer, 0, sizeof(buffer));
+ sprintf(buffer, "cddb query %08lx %d ", this->cda->disc_id, this->cda->num_tracks);
+ for(i = 0; i < this->cda->num_tracks; i++) {
+ sprintf(buffer, "%s%d ", buffer, this->cda->track[i].start);
+ }
+ sprintf(buffer, "%s%d\n", buffer, this->cda->track[i].length);
+ if((err = _cda_cddb_send_command(this, buffer)) > 0) {
+ memset(&buffer, 0, sizeof(buffer));
+ if(_cda_cddb_socket_read(&buffer[0], 2047, this->cddb.fd)) {
+ /* Parse returned code */
+ if((err = _cda_cddb_handle_code(buffer)) == 200) {
+ /* Disc entry exist */
+ char *m = NULL, *p = buffer;
+ int f = 0;
+
+ while((f <= 2) && ((m = xine_strsep(&p, " ")) != NULL)) {
+ if(f == 1)
+ this->cda->category = strdup(m);
+ else if(f == 2)
+ this->cda->cdiscid = strdup(m);
+ f++;
+ }
+ }
+
+ /* Now, grab track titles */
+ memset(&buffer, 0, sizeof(buffer));
+ sprintf(buffer, "cddb read %s %s\n", this->cda->category, this->cda->cdiscid);
+ if((err = _cda_cddb_send_command(this, buffer)) > 0) {
+ /* Get answer from read */
+ memset(&buffer, 0, sizeof(buffer));
+ if(_cda_cddb_socket_read(&buffer[0], 2047, this->cddb.fd)) {
+ /* Great, now we will have track titles */
+ if((err = _cda_cddb_handle_code(buffer)) == 210) {
+ char buf[2048];
+ unsigned char *pt;
+ int tnum;
+
+ while(strcmp(buffer, ".")) {
+ memset(&buffer, 0, sizeof(buffer));
+ _cda_cddb_socket_read(&buffer[0], 2047, this->cddb.fd);
+ if(sscanf(buffer, "DTITLE=%s", &buf[0]) == 1) {
+ pt = strrchr(buffer, '=');
+ if(pt) pt++;
+ this->cda->title = strdup(pt);
+ }
+ else if(sscanf(buffer, "TTITLE%d=%s", &tnum, &buf[0]) == 2) {
+ pt = strrchr(buffer, '=');
+ if(pt) pt++;
+ this->cda->track[tnum].title = strdup(pt);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ free(username);
+ free(hostname);
+}
+
+/*
+ * Open a socket.
+ */
+static int _cda_cddb_socket_open(cda_input_plugin_t *this) {
+ int sockfd;
+ struct hostent *he;
+ struct sockaddr_in their_addr;
+
+ if(this == NULL)
+ return -1;
+
+ this->cddb.server =
+ this->config->register_string(this->config, "input.cda_cddb_server", CDDB_SERVER,
+ "cddbp server name", NULL, NULL, NULL);
+
+ this->cddb.port =
+ this->config->register_num(this->config, "input.cda_cddb_port", CDDB_PORT,
+ "cddbp server port", NULL, NULL, NULL);
+
+
+ alarm(15);
+ signal(SIGALRM, die);
+ if((he=gethostbyname(this->cddb.server)) == NULL) {
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ return -1;
+ }
+
+ if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ return -1;
+ }
+
+ their_addr.sin_family = AF_INET;
+ their_addr.sin_port = htons(this->cddb.port);
+ their_addr.sin_addr = *((struct in_addr *)he->h_addr);
+ memset(&(their_addr.sin_zero), 0, 8);
+
+ if(connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ return -1;
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ return sockfd;
+}
+
+/*
+ * Close the socket
+ */
+static void _cda_cddb_socket_close(cda_input_plugin_t *this) {
+
+ if((this == NULL) || (this->cddb.fd < 0))
+ return;
+
+ close(this->cddb.fd);
+ this->cddb.fd = -1;
+}
+
+/*
+ *
+ */
+static unsigned int _cda_cddb_sum(int n) {
+ unsigned int ret = 0;
+
+ while(n > 0) {
+ ret += (n % 10);
+ n /= 10;
+ }
+ return ret;
+}
+
+/*
+ * Compute cddb disc compliant id
+ */
+static unsigned long _cda_calc_cddb_id(cdainfo_t *cda) {
+ int i, tsum = 0;
+
+ if(cda == NULL || (cda->num_tracks <= 0))
+ return 0;
+
+ for(i = 0; i < cda->num_tracks; i++)
+ tsum += _cda_cddb_sum((cda->track[i].start / 75));
+
+ return ((tsum % 0xff) << 24
+ | (cda->track[cda->num_tracks].length - (cda->track[0].start / 75)) << 8
+ | cda->num_tracks);
+}
+
+/*
+ * return cbbd disc id.
+ */
+static unsigned long _cda_get_cddb_id(cdainfo_t *cda) {
+
+ if(cda == NULL || (cda->num_tracks <= 0))
+ return 0;
+
+ return _cda_calc_cddb_id(cda);
+}
+
+/*
+ * grab (try) titles from cddb server.
+ */
+static void _cda_cbbd_grab_infos(cda_input_plugin_t *this) {
+
+ if(this == NULL)
+ return;
+
+ this->cddb.fd = _cda_cddb_socket_open(this);
+ if(this->cddb.fd >= 0) {
+ printf("input_cda: server '%s:%d' successfuly connected.\n",
+ this->cddb.server, this->cddb.port);
+
+ _cda_cddb_retrieve(this);
+ }
+ else
+ printf("input_cda: opening server '%s:%d' failed: %s\n",
+ this->cddb.server, this->cddb.port, strerror(errno));
+
+ _cda_cddb_socket_close(this);
+}
+
+/*
+ * **************** CDDB END *********************
+ */
+
+/*
* Get CDA status (pos, cur track, status)
* This function was grabbed and adapted from workbone (thanks to this team).
*/
@@ -420,8 +822,24 @@ static int _cda_read_toc_cd(cdainfo_t *cda) {
cda->first_track = hdr.cdth_trk0;
cda->num_tracks = hdr.cdth_trk1;
- if(cda->track)
+ if(cda->track) {
+ /* Freeing old track/disc titles */
+ for(i = 0; i < cda->num_tracks; i++) {
+ if(cda->track[i].title)
+ free(cda->track[i].title);
+ }
+
+ if(cda->title)
+ free(cda->title);
+
+ if(cda->category)
+ free(cda->category);
+
+ if(cda->cdiscid)
+ free(cda->cdiscid);
+
cda->track = (trackinfo_t *) realloc(cda->track, (cda->num_tracks + 1) * sizeof(trackinfo_t));
+ }
else
cda->track = (trackinfo_t *) malloc((cda->num_tracks + 1) * sizeof(trackinfo_t));
@@ -441,6 +859,7 @@ static int _cda_read_toc_cd(cdainfo_t *cda) {
cda->track[i].type = (entry.cdte_ctrl & CDROM_DATA_TRACK) ? CDDATA : CDAUDIO;
cda->track[i].length = entry.cdte_addr.msf.minute * 60 + entry.cdte_addr.msf.second;
cda->track[i].start = cda->track[i].length * 75 + entry.cdte_addr.msf.frame;
+ cda->track[i].title = NULL;
}
/* compute real track length */
@@ -452,12 +871,27 @@ static int _cda_read_toc_cd(cdainfo_t *cda) {
cda->track[i].length = (cda->track[i + 1].start - cda->track[i].start) * 2;
}
- cda->length = cda->track[cda->num_tracks].length;
+ cda->length = cda->track[cda->num_tracks].length;
+ cda->disc_id = _cda_get_cddb_id(cda);
+ cda->title = NULL;
+ cda->cdiscid = NULL;
+ cda->category = NULL;
#ifdef DEBUG_DISC
printf("Disc have %d track(s), first track is %d, length %d (%02d:%02d:%02d)\n",
cda->num_tracks, cda->first_track,
cda->length, (cda->length / (60 * 60)), ((cda->length / 60) % 60), (cda->length %60));
+
+ { /* CDDB infos */
+ int t;
+ printf("CDDB disc ID is %08lx\n", cda->disc_id);
+ printf("%d, ", cda->num_tracks);
+ for(t = 0; t < cda->num_tracks; t++) {
+ printf("%d, ", cda->track[t].start);
+ }
+ printf("%d\n", cda->track[t].length);
+ }
+
for(i = 0; i < cda->num_tracks; i++) {
printf("Track %2d, %s type, length %3d seconds(%02d:%02d:%02d), start at %3d secs\n",
i,
@@ -468,6 +902,13 @@ static int _cda_read_toc_cd(cdainfo_t *cda) {
(cda->track[i].length %60),
cda->track[i].start);
}
+ printf("LEADOUT (%2d), length %3d seconds(%02d:%02d:%02d), start at %3d secs\n",
+ i,
+ cda->track[i].length,
+ (cda->track[i].length / (60 * 60)),
+ ((cda->track[i].length / 60) % 60),
+ (cda->track[i].length %60),
+ cda->track[i].start);
#endif
return 1;
@@ -530,6 +971,26 @@ static void _cda_free_cda(cdainfo_t *cda) {
}
/*
+ * Change title in UI.
+ */
+static void _cda_update_ui_title(cda_input_plugin_t *this) {
+ xine_ui_event_t uievent;
+
+ if((this == NULL)
+ || (this->cda->title == NULL) || (this->cda->track[this->cda->cur_track - 1].title == NULL))
+ return;
+
+ memset(&this->cda->ui_title, 0, sizeof(this->cda->ui_title));
+ snprintf(this->cda->ui_title, 255, "%s -*- %d: %s", this->cda->title,
+ this->cda->cur_track, this->cda->track[this->cda->cur_track - 1].title);
+
+ uievent.event.type = XINE_EVENT_UI_SET_TITLE;
+ uievent.data = this->cda->ui_title;
+
+ xine_send_event(this->xine, &uievent.event);
+}
+
+/*
* *************************** END OF PRIVATES ************************************
*/
@@ -554,6 +1015,8 @@ static int cda_plugin_open (input_plugin_t *this_gen, char *mrl) {
_cda_free_cda(this->cda);
return 0;
}
+
+ _cda_cbbd_grab_infos(this);
filename = (char *) &mrl[6];
@@ -631,6 +1094,7 @@ static off_t cda_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin
switch (origin) {
case SEEK_SET:
_cda_play_track_from_pos(this->cda, this->cda->cur_track, (int) (offset/CDA_BLOCKSIZE));
+ _cda_update_ui_title(this);
break;
default:
@@ -757,6 +1221,8 @@ static mrl_t **cda_plugin_get_dir (input_plugin_t *this_gen,
if(!this->cda->num_tracks)
return NULL;
+ _cda_cbbd_grab_infos(this);
+
*nEntries = this->cda->num_tracks;
for(i=1; i <= this->cda->num_tracks; i++) {
@@ -828,6 +1294,8 @@ static char **cda_plugin_get_autoplay_list (input_plugin_t *this_gen, int *nFile
if(!this->cda->num_tracks)
return NULL;
+ _cda_cbbd_grab_infos(this);
+
*nFiles = this->cda->num_tracks;
for(i = 1; i <= this->cda->num_tracks; i++)
@@ -843,7 +1311,7 @@ static char **cda_plugin_get_autoplay_list (input_plugin_t *this_gen, int *nFile
*/
static char* cda_plugin_get_mrl (input_plugin_t *this_gen) {
cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen;
-
+
return this->mrl;
}
@@ -901,7 +1369,7 @@ input_plugin_t *init_input_plugin (int iface, xine_t *xine) {
this->xine = xine;
this->config = config;
-
+
this->mrl = NULL;
this->cda = (cdainfo_t *) xine_xmalloc(sizeof(cdainfo_t));
@@ -912,6 +1380,14 @@ input_plugin_t *init_input_plugin (int iface, xine_t *xine) {
"path to your local cd audio device file",
NULL, NULL, NULL));
+ this->cddb.server = config->register_string(config, "input.cda_cddb_server", CDDB_SERVER,
+ "cddbp server name", NULL, NULL, NULL);
+
+ this->cddb.port = config->register_num(config, "input.cda_cddb_port", CDDB_PORT,
+ "cddbp server port", NULL, NULL, NULL);
+
+ this->cddb.fd = -1;
+
this->mrls = (mrl_t **) xine_xmalloc(sizeof(mrl_t*));
this->mrls_allocated_entries = 0;