summaryrefslogtreecommitdiff
path: root/src/input/input_vcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/input/input_vcd.c')
-rw-r--r--src/input/input_vcd.c586
1 files changed, 586 insertions, 0 deletions
diff --git a/src/input/input_vcd.c b/src/input/input_vcd.c
new file mode 100644
index 000000000..42ccade80
--- /dev/null
+++ b/src/input/input_vcd.c
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2000-2001 the xine project
+ *
+ * This file is part of xine, a unix video player.
+ *
+ * xine is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * 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_vcd.c,v 1.1 2001/04/18 22:34:05 f1rmb Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#if defined (__linux__)
+#include <linux/cdrom.h>
+#elif defined (__FreeBSD__)
+#include <sys/cdio.h>
+#include <sys/cdrio.h>
+#else
+#error "you need to add cdrom / VCD support for your platform to input_vcd"
+#endif
+
+#include "xine.h"
+#include "monitor.h"
+#include "input_plugin.h"
+
+static uint32_t xine_debug;
+
+/* for FreeBSD make a link to the right devnode, like /dev/acd0c */
+#define CDROM "/dev/cdrom"
+#define VCDSECTORSIZE 2324
+
+typedef struct {
+ uint8_t sync [12];
+ uint8_t header [4];
+ uint8_t subheader [8];
+ uint8_t data [2324];
+ uint8_t spare [4];
+} cdsector_t;
+
+typedef struct {
+ int fd;
+
+#if defined (__linux__)
+ struct cdrom_tochdr tochdr;
+ struct cdrom_tocentry tocent[100];
+#elif defined (__FreeBSD__)
+ struct ioc_toc_header tochdr;
+ struct cd_toc_entry *tocent;
+ off_t cur_sector;
+#endif
+ int total_tracks;
+ int cur_track;
+
+#if defined (__linux__)
+ uint8_t cur_min, cur_sec, cur_frame;
+#endif
+
+ char *filelist[100];
+
+} input_vcd_t;
+
+static input_vcd_t gVCD;
+
+static void input_plugin_init (void) {
+ int i;
+
+ gVCD.fd = -1;
+ for (i=0; i<100; i++)
+ gVCD.filelist[i] = (char *) malloc (256);
+}
+
+
+#if defined (__linux__)
+static int input_vcd_read_toc (void) {
+ int i;
+
+ /* read TOC header */
+ if ( ioctl(gVCD.fd, CDROMREADTOCHDR, &gVCD.tochdr) == -1 ) {
+ fprintf (stderr, "input_vcd : error in ioctl CDROMREADTOCHDR\n");
+ return -1;
+ }
+
+ /* read individual tracks */
+ for (i=gVCD.tochdr.cdth_trk0; i<=gVCD.tochdr.cdth_trk1; i++) {
+ gVCD.tocent[i-1].cdte_track = i;
+ gVCD.tocent[i-1].cdte_format = CDROM_MSF;
+ if ( ioctl(gVCD.fd, CDROMREADTOCENTRY, &gVCD.tocent[i-1]) == -1 ) {
+ fprintf (stderr, "input_vcd: error in ioctl CDROMREADTOCENTRY\n");
+ return -1;
+ }
+ }
+
+ /* read the lead-out track */
+ gVCD.tocent[gVCD.tochdr.cdth_trk1].cdte_track = CDROM_LEADOUT;
+ gVCD.tocent[gVCD.tochdr.cdth_trk1].cdte_format = CDROM_MSF;
+
+ if (ioctl(gVCD.fd, CDROMREADTOCENTRY, &gVCD.tocent[gVCD.tochdr.cdth_trk1]) == -1 ) {
+ fprintf (stderr, "input_vcd: error in ioctl CDROMREADTOCENTRY\n");
+ return -1;
+ }
+
+ gVCD.total_tracks = gVCD.tochdr.cdth_trk1;
+
+ return 0;
+}
+#elif defined (__FreeBSD__)
+static int input_vcd_read_toc (void) {
+
+ struct ioc_read_toc_entry te;
+ int ntracks;
+
+ /* read TOC header */
+ if ( ioctl(gVCD.fd, CDIOREADTOCHEADER, &gVCD.tochdr) == -1 ) {
+ fprintf (stderr, "input_vcd : error in ioctl CDROMREADTOCHDR\n");
+ return -1;
+ }
+
+ ntracks = gVCD.tochdr.ending_track
+ - gVCD.tochdr.starting_track + 2;
+ gVCD.tocent = (struct cd_toc_entry *)malloc(sizeof(*gVCD.tocent) * ntracks);
+
+ te.address_format = CD_LBA_FORMAT;
+ te.starting_track = 0;
+ te.data_len = ntracks * sizeof(struct cd_toc_entry);
+ te.data = gVCD.tocent;
+
+ if ( ioctl(gVCD.fd, CDIOREADTOCENTRYS, &te) == -1 ){
+ fprintf (stderr, "input_vcd: error in ioctl CDROMREADTOCENTRY\n");
+ return -1;
+ }
+
+ gVCD.total_tracks = gVCD.tochdr.ending_track
+ - gVCD.tochdr.starting_track +1;
+
+ return 0;
+}
+#endif
+
+static int input_plugin_open (const char *mrl) {
+
+ char *filename;
+
+ if (strncasecmp (mrl, "vcd://",6))
+ return 0;
+
+ gVCD.fd = open (CDROM, O_RDONLY);
+
+ if (gVCD.fd == -1) {
+ return 0;
+ }
+
+ if (input_vcd_read_toc ()) {
+ close (gVCD.fd);
+ gVCD.fd = -1;
+ return 0;
+ }
+
+ filename = (char *) &mrl[6];
+
+ xprintf (VERBOSE|INPUT, "Opening >%s<\n",filename);
+
+ if (sscanf (filename, "%d", &gVCD.cur_track) != 1) {
+ fprintf (stderr, "input_vcd: malformed MRL. Use vcd://<track #>\n");
+ close (gVCD.fd);
+ gVCD.fd = -1;
+ return 0;
+ }
+
+ if (gVCD.cur_track>=gVCD.total_tracks) {
+ fprintf (stderr, "input_vcd: invalid track %d (valid range: 0 .. %d)\n",
+ gVCD.cur_track, gVCD.total_tracks-1);
+ close (gVCD.fd);
+ gVCD.fd = -1;
+ return 0;
+ }
+
+#if defined (__linux__)
+ gVCD.cur_min = gVCD.tocent[gVCD.cur_track].cdte_addr.msf.minute;
+ gVCD.cur_sec = gVCD.tocent[gVCD.cur_track].cdte_addr.msf.second;
+ gVCD.cur_frame = gVCD.tocent[gVCD.cur_track].cdte_addr.msf.frame;
+#elif defined (__FreeBSD__)
+ {
+ int bsize = 2352;
+ if (ioctl (gVCD.fd, CDRIOCSETBLOCKSIZE, &bsize) == -1) {
+ fprintf (stderr, "input_vcd: error in CDRIOCSETBLOCKSIZE %d\n", errno);
+ return 0;
+ }
+
+ gVCD.cur_sector =
+ ntohl(gVCD.tocent
+ [gVCD.cur_track+1 - gVCD.tochdr.starting_track].addr.lba);
+
+ }
+#endif
+
+ return 1;
+}
+
+
+
+#if defined (__linux__)
+static uint32_t input_plugin_read (char *buf, uint32_t nlen) {
+
+ static struct cdrom_msf msf ;
+ static cdsector_t data;
+ struct cdrom_msf0 *end_msf;
+
+ if (nlen != VCDSECTORSIZE)
+ return 0;
+
+ do
+ {
+ end_msf = &gVCD.tocent[gVCD.cur_track+1].cdte_addr.msf;
+
+ /*
+ printf ("cur: %02d:%02d:%02d end: %02d:%02d:%02d\n",
+ gVCD.cur_min, gVCD.cur_sec, gVCD.cur_frame,
+ end_msf->minute, end_msf->second, end_msf->frame);
+ */
+
+ if ( (gVCD.cur_min>=end_msf->minute) && (gVCD.cur_sec>=end_msf->second)
+ && (gVCD.cur_frame>=end_msf->frame))
+ return 0;
+
+ msf.cdmsf_min0 = gVCD.cur_min;
+ msf.cdmsf_sec0 = gVCD.cur_sec;
+ msf.cdmsf_frame0 = gVCD.cur_frame;
+
+ memcpy (&data, &msf, sizeof (msf));
+
+ if (ioctl (gVCD.fd, CDROMREADRAW, &data) == -1) {
+ fprintf (stderr, "input_vcd: error in CDROMREADRAW\n");
+ return 0;
+ }
+
+
+ gVCD.cur_frame++;
+ if (gVCD.cur_frame>=75) {
+ gVCD.cur_frame = 0;
+ gVCD.cur_sec++;
+ if (gVCD.cur_sec>=60) {
+ gVCD.cur_sec = 0;
+ gVCD.cur_min++;
+ }
+ }
+
+ /* Header ID check for padding sector. VCD uses this to keep constant
+ bitrate so the CD doesn't stop/start */
+ }
+ while((data.subheader[2]&~0x01)==0x60);
+
+ memcpy (buf, data.data, VCDSECTORSIZE); /* FIXME */
+ return VCDSECTORSIZE;
+}
+#elif defined (__FreeBSD__)
+static uint32_t input_plugin_read (char *buf, uint32_t nlen) {
+ static cdsector_t data;
+ int bsize = 2352;
+
+ if (nlen != VCDSECTORSIZE)
+ return 0;
+
+ do {
+ if (lseek (gVCD.fd, gVCD.cur_sector * bsize, SEEK_SET) == -1) {
+ fprintf (stderr, "input_vcd: seek error %d\n", errno);
+ return 0;
+ }
+ if (read (gVCD.fd, &data, bsize) == -1) {
+ fprintf (stderr, "input_vcd: read error %d\n", errno);
+ return 0;
+ }
+ gVCD.cur_sector++;
+ } while ((data.subheader[2]&~0x01)==0x60);
+ memcpy (buf, data.data, VCDSECTORSIZE);
+ return VCDSECTORSIZE;
+}
+#endif
+
+
+#if defined (__linux__)
+static off_t input_plugin_seek (off_t offset, int origin) {
+
+ struct cdrom_msf0 *start_msf;
+ uint32_t dist ;
+ off_t sector_pos;
+
+ start_msf = &gVCD.tocent[gVCD.cur_track].cdte_addr.msf;
+
+ switch (origin) {
+ case SEEK_SET:
+ dist = offset / VCDSECTORSIZE;
+
+ gVCD.cur_min = dist / (60*75) + start_msf->minute;
+ dist %= 60;
+ gVCD.cur_sec = dist / 75 + start_msf->second;
+ dist %= 75;
+ gVCD.cur_frame = dist + start_msf->frame;
+
+ xprintf (VERBOSE|INPUT, "%d => %02d:%02d:%02d\n",offset,gVCD.cur_min,gVCD.cur_sec,gVCD.cur_frame);
+
+ break;
+ case SEEK_CUR:
+ if (offset)
+ fprintf (stderr, "input_vcd: SEEK_CUR not implemented for offset != 0\n");
+
+ sector_pos = 75 - start_msf->frame;
+
+ if (start_msf->second<60)
+ sector_pos += (59 - start_msf->second) * 75;
+
+ if ( gVCD.cur_min > start_msf->minute) {
+ sector_pos += (gVCD.cur_min - start_msf->minute-1) * 60 * 75;
+
+ sector_pos += gVCD.cur_sec * 60;
+
+ sector_pos += gVCD.cur_frame ;
+ }
+
+ return sector_pos * VCDSECTORSIZE;
+
+ break;
+ default:
+ fprintf (stderr, "input_vcd: error seek to origin %d not implemented!\n",
+ origin);
+ return 0;
+ }
+
+ return offset ; /* FIXME */
+}
+#elif defined (__FreeBSD__)
+static off_t input_plugin_seek (off_t offset, int origin) {
+
+
+ u_long start;
+ uint32_t dist ;
+ off_t sector_pos;
+
+ start =
+ ntohl(gVCD.tocent
+ [gVCD.cur_track+1 - gVCD.tochdr.starting_track].addr.lba);
+
+ /* printf("seek: start sector:%lu, origin: %d, offset:%qu\n",
+ start, origin, offset);
+ */
+
+ switch (origin) {
+ case SEEK_SET:
+ dist = offset / VCDSECTORSIZE;
+ gVCD.cur_sector = start + dist;
+ break;
+ case SEEK_CUR:
+
+ if (offset)
+ fprintf (stderr, "input_vcd: SEEK_CUR not implemented for offset != 0\n");
+
+ sector_pos = gVCD.cur_sector;
+
+ return sector_pos * VCDSECTORSIZE;
+
+ break;
+ default:
+ fprintf (stderr, "input_vcd: error seek to origin %d not implemented!\n",
+ origin);
+ return 0;
+ }
+
+ return offset ; /* FIXME */
+}
+#endif
+
+#if defined (__linux__)
+static off_t input_plugin_get_length (void) {
+ struct cdrom_msf0 *end_msf, *start_msf;
+ off_t len ;
+
+ start_msf = &gVCD.tocent[gVCD.cur_track].cdte_addr.msf;
+ end_msf = &gVCD.tocent[gVCD.cur_track+1].cdte_addr.msf;
+
+ len = 75 - start_msf->frame;
+
+ if (start_msf->second<60)
+ len += (59 - start_msf->second) * 75;
+
+ if (end_msf->minute > start_msf->minute) {
+ len += (end_msf->minute - start_msf->minute-1) * 60 * 75;
+
+ len += end_msf->second * 60;
+
+ len += end_msf->frame ;
+ }
+
+ return len * VCDSECTORSIZE;
+}
+#elif defined (__FreeBSD__)
+static off_t input_plugin_get_length (void) {
+
+ off_t len ;
+
+
+ len =
+ ntohl(gVCD.tocent
+ [gVCD.cur_track+2
+ - gVCD.tochdr.starting_track].addr.lba)
+ - ntohl(gVCD.tocent
+ [gVCD.cur_track+1
+ - gVCD.tochdr.starting_track].addr.lba);
+
+ return len * 2352; /*VCDSECTORSIZE;*/
+
+}
+#endif
+
+static uint32_t input_plugin_get_capabilities (void) {
+ return INPUT_CAP_SEEKABLE | INPUT_CAP_BLOCK | INPUT_CAP_AUTOPLAY;
+}
+
+static uint32_t input_plugin_get_blocksize (void) {
+ return VCDSECTORSIZE;
+}
+
+#if defined (__linux__)
+static int input_plugin_eject (void) {
+ int ret, status;
+
+ if((gVCD.fd = open(CDROM, O_RDONLY|O_NONBLOCK)) > -1) {
+ if((status = ioctl(gVCD.fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) {
+ switch(status) {
+ case CDS_TRAY_OPEN:
+ if((ret = ioctl(gVCD.fd, CDROMCLOSETRAY)) != 0) {
+ xprintf(VERBOSE|INPUT, "CDROMCLOSETRAY failed: %s\n", strerror(errno));
+ }
+ break;
+ case CDS_DISC_OK:
+ if((ret = ioctl(gVCD.fd, CDROMEJECT)) != 0) {
+ xprintf(VERBOSE|INPUT, "CDROMEJECT failed: %s\n", strerror(errno));
+ }
+ break;
+ }
+ }
+ else {
+ xprintf(VERBOSE|INPUT, "CDROM_DRIVE_STATUS failed: %s\n",
+ strerror(errno));
+ close(gVCD.fd);
+ return 0;
+ }
+ }
+
+ close(gVCD.fd);
+
+ return 1;
+}
+#elif defined (__FreeBSD__)
+static int input_plugin_eject (void) {
+ int fd;
+
+ if ((fd = open(CDROM, O_RDONLY|O_NONBLOCK)) > -1) {
+ if (ioctl(fd, CDIOCALLOW) == -1) {
+ perror("ioctl(cdromallow)");
+ } else {
+ if (ioctl(fd, CDIOCEJECT) == -1) {
+ perror("ioctl(cdromeject)");
+ }
+ }
+ close(fd);
+ }
+
+ return 1;
+}
+#endif
+
+static void input_plugin_close (void) {
+ xprintf (VERBOSE|INPUT, "closing input\n");
+
+ close(gVCD.fd);
+ gVCD.fd = -1;
+}
+
+static char *input_plugin_get_identifier (void) {
+ return "VCD";
+}
+
+static char **input_plugin_get_autoplay_list (int *nFiles) {
+
+ int i;
+
+ gVCD.fd = open (CDROM, O_RDONLY);
+
+ if (gVCD.fd == -1) {
+ perror ("unable to open /dev/cdrom");
+ return NULL;
+ }
+
+ if (input_vcd_read_toc ()) {
+ close (gVCD.fd);
+ gVCD.fd = -1;
+
+ printf ("vcd_read_toc failed\n");
+
+ return NULL;
+ }
+
+ close (gVCD.fd);
+ gVCD.fd = -1;
+
+ *nFiles = gVCD.total_tracks;
+
+ /* printf ("%d tracks\n",gVCD.total_tracks); */
+
+ for (i=1; i<gVCD.total_tracks; i++) { /* FIXME: check if track 0 contains valid data */
+ sprintf (gVCD.filelist[i-1], "vcd://%d",i);
+ /* printf ("list[%d] : %d %s\n", i, gVCD.filelist[i-1], gVCD.filelist[i-1]); */
+ }
+
+ return gVCD.filelist;
+}
+
+static int input_plugin_is_branch_possible (const char *next_mrl) {
+
+ char *filename;
+ int track;
+
+ if (strncasecmp (next_mrl, "vcd://",6))
+ return 0;
+
+ filename = (char *) &next_mrl[6];
+
+ if (sscanf (filename, "%d", &track) != 1) {
+ return 0;
+ }
+
+ if ((track>=gVCD.total_tracks) || (track != (gVCD.cur_track+1)))
+ return 0;
+
+ return 1;
+}
+
+
+static input_plugin_t plugin_op = {
+ NULL,
+ NULL,
+ input_plugin_init,
+ input_plugin_open,
+ input_plugin_read,
+ input_plugin_seek,
+ input_plugin_get_length,
+ input_plugin_get_capabilities,
+ NULL,
+ input_plugin_get_blocksize,
+ input_plugin_eject,
+ input_plugin_close,
+ input_plugin_get_identifier,
+ input_plugin_get_autoplay_list,
+ input_plugin_is_branch_possible,
+ NULL
+};
+
+input_plugin_t *input_plugin_getinfo(uint32_t dbglvl) {
+
+ xine_debug = dbglvl;
+
+ return &plugin_op;
+}
+
+