summaryrefslogtreecommitdiff
path: root/src/input
diff options
context:
space:
mode:
Diffstat (limited to 'src/input')
-rw-r--r--src/input/Makefile.am72
-rw-r--r--src/input/dvd_udf.c661
-rw-r--r--src/input/dvd_udf.h39
-rw-r--r--src/input/input_dvd.c438
-rw-r--r--src/input/input_file.c191
-rw-r--r--src/input/input_net.c224
-rw-r--r--src/input/input_plugin.h178
-rw-r--r--src/input/input_rtp.c384
-rw-r--r--src/input/input_stdin_fifo.c219
-rw-r--r--src/input/input_vcd.c586
10 files changed, 2992 insertions, 0 deletions
diff --git a/src/input/Makefile.am b/src/input/Makefile.am
new file mode 100644
index 000000000..b30549404
--- /dev/null
+++ b/src/input/Makefile.am
@@ -0,0 +1,72 @@
+##
+## Process this file with automake to produce Makefile.in
+##
+
+#CFLAGS += @GLOBAL_CFLAGS@
+CFLAGS += -D_FILE_OFFSET_BITS=64 -Wall -DXINE_COMPILE
+
+LIBTOOL = $(SHELL) $(top_builddir)/libtool-nofpic
+
+libdir = $(XINE_PLUGINDIR)
+
+#lib_LTLIBRARIES = input_file.la input_net.la input_dvd.la input_vcd.la \
+# input_stdin_fifo.la
+lib_LTLIBRARIES = input_file.la
+
+#input_dvd_la_SOURCES = input_dvd.c dvd_udf.c
+#input_dvd_la_LDFLAGS = -avoid-version -module -Wl,-soname,input_dvd.so
+#input_dvd_la_LIBADD =
+
+input_file_la_SOURCES = input_file.c
+input_file_la_LDFLAGS = -avoid-version -module -Wl,-soname,input_file.so
+input_file_la_LIBADD =
+
+#input_net_la_SOURCES = input_net.c
+#input_net_la_LDFLAGS = -avoid-version -module -Wl,-soname,input_net.so
+#input_net_la_LIBADD =
+
+#input_vcd_la_SOURCES = input_vcd.c
+#input_vcd_la_LDFLAGS = -avoid-version -module -Wl,-soname,input_vcd.so
+#input_vcd_la_LIBADD =
+
+#input_stdin_fifo_la_SOURCES = input_stdin_fifo.c
+#input_stdin_fifo_la_LDFLAGS = -avoid-version -module -Wl,-soname,input_stdin_fifo.so
+
+
+include_HEADERS = input_plugin.h
+noinst_HEADERS = dvd_udf.h
+
+
+##
+## Install header files (default=$includedir/xine)
+##
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(includedir)/xine
+ @list='$(include_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d= ; else d="$(srcdir)/"; fi; \
+ echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/xine/$$p"; \
+ $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/xine/$$p; \
+ done
+
+
+##
+## Remove them
+##
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ list='$(include_HEADERS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(includedir)/xine/$$p; \
+ done
+
+
+debug:
+ $(MAKE) CFLAGS="-D_FILE_OFFSET_BITS=64 -Wall -DDEBUG -g"
+
+mostlyclean-generic:
+ -rm -f *~ \#* .*~ .\#*
+
+maintainer-clean-generic:
+ -@echo "This command is intended for maintainers to use;"
+ -@echo "it deletes files that may require special tools to rebuild."
+ -rm -f Makefile.in
diff --git a/src/input/dvd_udf.c b/src/input/dvd_udf.c
new file mode 100644
index 000000000..e2a0692b4
--- /dev/null
+++ b/src/input/dvd_udf.c
@@ -0,0 +1,661 @@
+/*
+ * dvdudf: parse and read the UDF volume information of a DVD Video
+ * Copyright (C) 1999 Christian Wolff for convergence integrated media GmbH
+ * minor modifications by Thomas Mirlacher
+ * dir support and bugfixes by Guenter Bartsch for use in xine
+ *
+ * This program 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.
+ *
+ * This program 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ * The author can be reached at scarabaeus@convergence.de,
+ * the project's page is at http://linuxtv.org/dvd/
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "dvd_udf.h"
+
+static int _Unicodedecode (uint8_t *data, int len, char *target);
+
+#define MAX_FILE_LEN 2048
+
+struct Partition {
+ int valid;
+ uint8_t VolumeDesc[128];
+ uint16_t Flags;
+ uint16_t Number;
+ uint8_t Contents[32];
+ uint32_t AccessType;
+ uint32_t Start;
+ uint32_t Length;
+} partition;
+
+struct AD {
+ uint32_t Location;
+ uint32_t Length;
+ uint8_t Flags;
+ uint16_t Partition;
+};
+
+/* for direct data access, LSB first */
+#define GETN1(p) ((uint8_t)data[p])
+#define GETN2(p) ((uint16_t)data[p]|((uint16_t)data[(p)+1]<<8))
+#define GETN4(p) ((uint32_t)data[p]|((uint32_t)data[(p)+1]<<8)|((uint32_t)data[(p)+2]<<16)|((uint32_t)data[(p)+3]<<24))
+#define GETN(p,n,target) memcpy(target,&data[p],n)
+
+
+/*
+ * reads absolute Logical Block of the disc
+ * returns number of read bytes on success, 0 on error
+ */
+
+int UDFReadLB (int fd, off_t lb_number, size_t block_count, uint8_t *data)
+{
+ if (fd < 0)
+ return 0;
+
+ if (lseek (fd, lb_number * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET) < 0)
+ return 0; /* position not found */
+
+ return read (fd, data, block_count*DVD_VIDEO_LB_LEN);
+}
+
+
+static int _Unicodedecode (uint8_t *data, int len, char *target)
+{
+ int p=1,i=0;
+
+ if (!(data[0] & 0x18)) {
+ target[0] ='\0';
+ return 0;
+ }
+
+ if (data[0] & 0x10) { /* ignore MSB of unicode16 */
+ p++;
+
+ while (p<len)
+ target[i++]=data[p+=2];
+ } else {
+ while (p<len)
+ target[i++]=data[p++];
+ }
+
+ target[i]='\0';
+
+ return 0;
+}
+
+
+int UDFEntity (uint8_t *data, uint8_t *Flags, char *Identifier)
+{
+ Flags[0] = data[0];
+ strncpy (Identifier, &data[1], 5);
+
+ return 0;
+}
+
+
+int UDFDescriptor (uint8_t *data, uint16_t *TagID)
+{
+ TagID[0] = GETN2(0);
+ /* TODO: check CRC n stuff */
+
+ return 0;
+}
+
+
+int UDFExtentAD (uint8_t *data, uint32_t *Length, uint32_t *Location)
+{
+ Length[0] =GETN4(0);
+ Location[0]=GETN4(4);
+
+ return 0;
+}
+
+#define UDFADshort 1
+#define UDFADlong 2
+#define UDFADext 4
+
+int UDFAD (uint8_t *data, struct AD *ad, uint8_t type)
+{
+ ad->Length = GETN4(0);
+ ad->Flags = ad->Length>>30;
+ ad->Length &= 0x3FFFFFFF;
+
+ switch (type) {
+ case UDFADshort:
+ ad->Location = GETN4(4);
+ ad->Partition = partition.Number; /* use number of current partition */
+ break;
+ case UDFADlong:
+ ad->Location = GETN4(4);
+ ad->Partition = GETN2(8);
+ break;
+ case UDFADext:
+ ad->Location = GETN4(12);
+ ad->Partition = GETN2(16);
+ break;
+ }
+
+ return 0;
+}
+
+int UDFICB (uint8_t *data, uint8_t *FileType, uint16_t *Flags)
+{
+ FileType[0]=GETN1(11);
+ Flags[0]=GETN2(18);
+
+ return 0;
+}
+
+int UDFPartition (uint8_t *data, uint16_t *Flags, uint16_t *Number, char *Contents,
+ uint32_t *Start, uint32_t *Length)
+{
+ Flags[0] = GETN2(20);
+ Number[0] = GETN2(22);
+ GETN(24,32,Contents);
+ Start[0] = GETN4(188);
+ Length[0] = GETN4(192);
+
+ return 0;
+}
+
+
+/*
+ * reads the volume descriptor and checks the parameters
+ * returns 0 on OK, 1 on error
+ */
+
+int UDFLogVolume (uint8_t *data, char *VolumeDescriptor)
+{
+ uint32_t lbsize,MT_L,N_PM;
+
+ _Unicodedecode (&data[84],128,VolumeDescriptor);
+ lbsize = GETN4(212); /* should be 2048 */
+ MT_L = GETN4(264); /* should be 6 */
+ N_PM = GETN4(268); /* should be 1 */
+
+ if (lbsize!=DVD_VIDEO_LB_LEN)
+ return 1;
+
+ return 0;
+}
+
+
+int UDFFileEntry (uint8_t *data, uint8_t *FileType, struct AD *ad)
+{
+ uint8_t filetype;
+ uint16_t flags;
+ uint32_t L_EA,L_AD;
+ int p;
+
+ UDFICB(&data[16],&filetype,&flags);
+ FileType[0]=filetype;
+ L_EA=GETN4(168);
+ L_AD=GETN4(172);
+ p=176+L_EA;
+
+ while (p<176+L_EA+L_AD) {
+ switch (flags&0x07) {
+ case 0:
+ UDFAD (&data[p], ad, UDFADshort);
+ p += 0x08;
+ break;
+ case 1:
+ UDFAD (&data[p], ad, UDFADlong);
+ p += 0x10;
+ break;
+ case 2: UDFAD (&data[p], ad, UDFADext);
+ p += 0x14;
+ break;
+ case 3:
+ switch (L_AD) {
+ case 0x08:
+ UDFAD (&data[p], ad, UDFADshort);
+ break;
+ case 0x10:
+ UDFAD (&data[p], ad, UDFADlong);
+ break;
+ case 0x14:
+ UDFAD (&data[p], ad, UDFADext);
+ break;
+ }
+ default:
+ p += L_AD;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+int UDFFileIdentifier (uint8_t *data, uint8_t *FileCharacteristics, char *FileName, struct AD *FileICB)
+{
+ uint8_t L_FI;
+ uint16_t L_IU;
+
+ FileCharacteristics[0]=GETN1(18);
+ L_FI=GETN1(19);
+ UDFAD(&data[20],FileICB,UDFADlong);
+ L_IU=GETN2(36);
+
+ if (L_FI)
+ _Unicodedecode (&data[38+L_IU],L_FI,FileName);
+ else
+ FileName[0]='\0';
+
+ return 4*((38+L_FI+L_IU+3)/4);
+}
+
+
+/*
+ * Maps ICB to FileAD
+ * ICB: Location of ICB of directory to scan
+ * FileType: Type of the file
+ * File: Location of file the ICB is pointing to
+ * return 1 on success, 0 on error;
+ */
+
+int UDFMapICB (int fd, struct AD ICB, uint8_t *FileType, struct AD *File)
+{
+ uint8_t *LogBlock;
+ uint32_t lbnum;
+ uint16_t TagID;
+
+ if ((LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN)) == NULL) {
+ fprintf(stderr, "%s: malloc failed\n", __FUNCTION__);
+ return 0;
+ }
+
+ lbnum=partition.Start+ICB.Location;
+
+ do {
+ if (!UDFReadLB(fd, lbnum++,1,LogBlock)) TagID=0;
+ else UDFDescriptor(LogBlock,&TagID);
+
+ if (TagID==261) {
+ UDFFileEntry(LogBlock,FileType,File);
+ free(LogBlock);
+ return 1;
+ };
+ } while ((lbnum<=partition.Start+ICB.Location+(ICB.Length-1)/DVD_VIDEO_LB_LEN) && (TagID!=261));
+
+ free(LogBlock);
+ return 0;
+}
+
+/*
+ * Dir: Location of directory to scan
+ * FileName: Name of file to look for
+ * FileICB: Location of ICB of the found file
+ * return 1 on success, 0 on error;
+ */
+
+int UDFScanDir (int fd, struct AD Dir, char *FileName, struct AD *FileICB)
+{
+ uint8_t *LogBlock;
+ uint32_t lbnum, lb_dir_end, offset;
+ uint16_t TagID;
+ uint8_t filechar;
+ char *filename;
+ int p, retval = 0;
+
+ LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN * 30);
+ filename = (char*)malloc(MAX_FILE_LEN);
+ if ((LogBlock == NULL) || (filename == NULL)) {
+ fprintf(stderr, "%s: malloc failed\n", __FUNCTION__);
+ goto bail;
+ }
+
+ /*
+ * read complete directory
+ */
+
+ lbnum = partition.Start+Dir.Location;
+ lb_dir_end = partition.Start+Dir.Location+(Dir.Length-1)/DVD_VIDEO_LB_LEN;
+ offset = 0;
+
+ while (lbnum<=lb_dir_end) {
+
+ if (!UDFReadLB(fd, lbnum++,1,&LogBlock[offset]))
+ break;
+
+ offset += DVD_VIDEO_LB_LEN;
+ }
+
+ /* Scan dir for ICB of file */
+
+ p=0;
+ while (p<offset) {
+ UDFDescriptor (&LogBlock[p],&TagID);
+
+ if (TagID==257) {
+ p += UDFFileIdentifier(&LogBlock[p],&filechar,filename,FileICB);
+ if (!strcasecmp (FileName,filename)) {
+ retval = 1;
+ goto bail;
+ }
+ } else
+ p=offset;
+ }
+
+ retval = 0;
+
+bail:
+ free(LogBlock);
+ free(filename);
+ return retval;
+}
+
+
+/*
+ * looks for partition on the disc
+ * partnum: number of the partition, starting at 0
+ * part: structure to fill with the partition information
+ * return 1 if partition found, 0 on error;
+ */
+
+int UDFFindPartition (int fd, int partnum, struct Partition *part)
+{
+ uint8_t *LogBlock,*Anchor;
+ uint32_t lbnum,MVDS_location,MVDS_length;
+ uint16_t TagID;
+ uint32_t lastsector;
+ int i,terminate,volvalid,retval = 0;
+
+ LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN);
+ Anchor = (uint8_t*)malloc(DVD_VIDEO_LB_LEN);
+ if ((LogBlock == NULL) || (Anchor == NULL)) {
+ fprintf(stderr, "%s: malloc failed\n", __FUNCTION__);
+ goto bail;
+ }
+
+ /* find anchor */
+ lastsector=0;
+ lbnum=256; /* try #1, prime anchor */
+ terminate=0;
+
+ while (1) { /* loop da loop */
+ if (UDFReadLB(fd, lbnum,1,Anchor)) {
+ UDFDescriptor(Anchor,&TagID);
+ } else
+ TagID=0;
+
+ if (TagID!=2) { /* not an anchor? */
+ if (terminate) goto bail; /* final try failed */
+ if (lastsector) { /* we already found the last sector */
+ lbnum=lastsector; /* try #3, alternative backup anchor */
+ terminate=1; /* but thats just about enough, then! */
+ } else {
+ /* TODO: find last sector of the disc (this is optional) */
+ if (lastsector) lbnum=lastsector-256; /* try #2, backup anchor */
+ else goto bail; /* unable to find last sector */
+ }
+ } else break; /* it is an anchor! continue... */
+ }
+
+ UDFExtentAD(&Anchor[16],&MVDS_length,&MVDS_location); /* main volume descriptor */
+
+ part->valid=0;
+ volvalid=0;
+ part->VolumeDesc[0]='\0';
+
+ i=1;
+ do {
+ /* Find Volume Descriptor */
+ lbnum=MVDS_location;
+ do {
+ if (!UDFReadLB (fd, lbnum++, 1, LogBlock))
+ TagID=0;
+ else
+ UDFDescriptor (LogBlock, &TagID);
+ if ((TagID==5) && (!part->valid)) { /* Partition Descriptor */
+ UDFPartition (LogBlock,&part->Flags,&part->Number,part->Contents,
+ &part->Start,&part->Length);
+ part->valid=(partnum==part->Number);
+ } else if ((TagID==6) && (!volvalid)) { /* Logical Volume Descriptor */
+ if (UDFLogVolume(LogBlock,part->VolumeDesc)) {
+ /* TODO: sector size wrong! */
+ } else volvalid=1;
+ }
+ } while ((lbnum<=MVDS_location+(MVDS_length-1)/DVD_VIDEO_LB_LEN) && (TagID!=8) && ((!part->valid) || (!volvalid)));
+ if ((!part->valid) || (!volvalid)) UDFExtentAD(&Anchor[24],&MVDS_length,&MVDS_location); /* backup volume descriptor */
+ } while (i-- && ((!part->valid) || (!volvalid)));
+
+ retval = part->valid; /* we only care for the partition, not the volume */
+
+bail:
+ free(LogBlock);
+ free(Anchor);
+ return retval;
+}
+
+
+/*
+ * looks for a file on the UDF disc/imagefile and seeks to it's location
+ * filename has to be the absolute pathname on the UDF filesystem,
+ * starting with '/'
+ * returns absolute LB number, or 0 on error
+ */
+
+uint32_t UDFFindFile (int fd, char *filename, off_t *size)
+{
+ uint8_t *LogBlock;
+ uint8_t filetype;
+ uint32_t lbnum, retval = 0;
+ uint16_t TagID;
+ struct AD RootICB,File,ICB;
+ char *tokenline;
+ char *token;
+ off_t lb_number;
+ int Partition=0; /* this is the standard location for DVD Video */
+
+ LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN);
+ tokenline = (char*)malloc(MAX_FILE_LEN);
+ if ((LogBlock == NULL) || (tokenline == NULL)) {
+ fprintf(stderr, "%s: malloc failed\n", __FUNCTION__);
+ goto bail;
+ }
+ memset(tokenline, 0, MAX_FILE_LEN);
+
+ strncat (tokenline,filename,MAX_FILE_LEN);
+
+ /* Find partition */
+ if (!UDFFindPartition(fd, Partition,&partition))
+ goto bail;
+
+ /* Find root dir ICB */
+ lbnum=partition.Start;
+
+ do {
+ if (!UDFReadLB(fd, lbnum++,1,LogBlock))
+ TagID=0;
+ else
+ UDFDescriptor(LogBlock,&TagID);
+
+ if (TagID==256) /* File Set Descriptor */
+ UDFAD(&LogBlock[400],&RootICB,UDFADlong);
+ } while ((lbnum<partition.Start+partition.Length) && (TagID!=8) && (TagID!=256));
+ if (TagID!=256)
+ goto bail;
+ if (RootICB.Partition!=Partition)
+ goto bail;
+
+ /* Find root dir */
+ if (!UDFMapICB(fd, RootICB,&filetype,&File))
+ goto bail;
+ if (filetype!=4) /* root dir should be dir */
+ goto bail;
+
+ /* Tokenize filepath */
+ token=strtok(tokenline,"/");
+ while (token) {
+ if (!UDFScanDir(fd, File,token,&ICB))
+ goto bail;
+ if (!UDFMapICB(fd, ICB,&filetype,&File))
+ goto bail;
+ token=strtok(NULL,"/");
+ }
+
+ *size = File.Length;
+
+ lb_number = partition.Start+File.Location ;
+
+ printf ("lb_number : %ld\n", (long int)lb_number);
+
+ retval = lb_number;
+
+bail:
+ free(LogBlock);
+ free(tokenline);
+ return retval;
+}
+
+
+/*
+ * lists contents of given directory
+ */
+
+void UDFListDir(int fd, char *dirname, int nMaxFiles, char **file_list, int *nFiles) {
+ uint8_t *LogBlock;
+ uint32_t lbnum;
+ uint16_t TagID;
+ struct AD RootICB,Dir,ICB;
+ char *tokenline;
+ char *token, *ntoken;
+ uint8_t filetype;
+ char *filename;
+ int p;
+ uint8_t filechar;
+ char *dest;
+ int Partition=0; /* this is the standard location for DVD Video */
+
+ LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN * 30);
+ tokenline = (char*)malloc(MAX_FILE_LEN);
+ filename = (char*)malloc(MAX_FILE_LEN);
+ if ((LogBlock == NULL) || (tokenline == NULL) || (filename == NULL)) {
+ fprintf(stderr, "%s: malloc failed\n", __FUNCTION__);
+ goto bail;
+ }
+
+ *nFiles = 0;
+ tokenline[0]='\0';
+ strncat(tokenline,dirname,MAX_FILE_LEN);
+
+ /* Find partition */
+ if (!UDFFindPartition(fd, Partition,&partition))
+ goto bail; /* no partition found (no disc ??) */
+
+ /* Find root dir ICB */
+ lbnum=partition.Start;
+ do {
+ if (!UDFReadLB(fd, lbnum++,1,LogBlock))
+ TagID=0;
+ else
+ UDFDescriptor(LogBlock,&TagID);
+
+ if (TagID==256) { // File Set Descriptor
+ UDFAD(&LogBlock[400],&RootICB,UDFADlong);
+ }
+ } while ((lbnum<partition.Start+partition.Length)
+ && (TagID!=8) && (TagID!=256));
+
+ if (TagID!=256)
+ goto bail;
+ if (RootICB.Partition!=Partition)
+ goto bail;
+
+ /* Find root dir */
+ if (!UDFMapICB(fd, RootICB,&filetype,&Dir))
+ goto bail;
+ if (filetype!=4)
+ goto bail; /* root dir should be dir */
+
+
+
+ /* Tokenize filepath */
+ token=strtok(tokenline,"/");
+ ntoken=strtok(NULL,"/");
+ while (token != NULL) {
+
+ if (!UDFScanDir(fd, Dir,token,&ICB))
+ goto bail;
+ if (!UDFMapICB(fd, ICB,&filetype,&Dir))
+ goto bail;
+
+ if (ntoken == NULL) {
+ uint32_t lb_dir_end, offset;
+
+ /*
+ * read complete directory
+ */
+
+ lbnum = partition.Start+Dir.Location;
+ lb_dir_end = partition.Start+Dir.Location+(Dir.Length-1)/DVD_VIDEO_LB_LEN;
+ offset = 0;
+
+ while (lbnum<=lb_dir_end) {
+
+ if (!UDFReadLB(fd, lbnum++,1,&LogBlock[offset]))
+ break;
+
+ offset += DVD_VIDEO_LB_LEN;
+ }
+
+
+ p=0;
+ while (p<offset) {
+ UDFDescriptor(&LogBlock[p],&TagID);
+ /* printf ("tagid : %d\n",TagID); */
+ if (TagID==257) {
+ p+=UDFFileIdentifier(&LogBlock[p],&filechar,filename,&ICB);
+
+ /* printf ("file : >%s< %d (p: %d)\n", filename, *nFiles,p); */
+
+ if (strcmp (filename,"")) {
+
+ dest = file_list[*nFiles];
+ strncpy (dest,filename,256);
+ (*nFiles)++;
+
+ if ((*nFiles)>=nMaxFiles)
+ goto bail;
+
+ }
+
+ } else {
+ p=offset;
+ }
+ }
+
+ }
+
+ token=ntoken;
+ ntoken=strtok(NULL,"/");
+ }
+
+bail:
+ free(LogBlock);
+ free(tokenline);
+ free(filename);
+ return;
+}
+
diff --git a/src/input/dvd_udf.h b/src/input/dvd_udf.h
new file mode 100644
index 000000000..5db31b1fc
--- /dev/null
+++ b/src/input/dvd_udf.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2000-2001 plitsch-platsch
+ *
+ * xine_dvd_plugin 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_dvd_plugin 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
+ *
+ */
+
+#ifndef DVD_UDF_H
+#define DVD_UDF_H
+
+#include <inttypes.h>
+
+#define DVD_UDF_VERSION 19991115
+
+/*
+ * The length of one Logical Block of a DVD Video
+ */
+
+#define DVD_VIDEO_LB_LEN 2048
+
+int UDFReadLB (int fd, off_t lb_number, size_t block_count, uint8_t *data);
+
+uint32_t UDFFindFile(int fd, char *filename, off_t *size);
+
+void UDFListDir(int fd, char *dirname, int nMaxFiles, char **file_list, int *nFiles) ;
+
+#endif /* DVD_UDF_H */
diff --git a/src/input/input_dvd.c b/src/input/input_dvd.c
new file mode 100644
index 000000000..b3f105bbd
--- /dev/null
+++ b/src/input/input_dvd.c
@@ -0,0 +1,438 @@
+/*
+ * 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_dvd.c,v 1.1 2001/04/18 22:34:04 f1rmb Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <fcntl.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
+# include <sys/cdio.h>
+#elif defined(__linux__)
+# include <linux/cdrom.h>
+#else
+# error "Need the DVD ioctls"
+#endif
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "xine.h"
+#include "monitor.h"
+#include "input_plugin.h"
+#include "dvd_udf.h"
+
+static uint32_t xine_debug;
+
+#define DVD "/dev/dvd"
+#define RDVD "/dev/rdvd"
+
+/*
+ * global Variables:
+ */
+
+static int dvd_fd, raw_fd;
+static off_t file_size, file_size_left;
+static int file_lbstart, file_lbcur;
+static int gVTSMinor, gVTSMajor;
+
+/*
+ * udf dir function
+ */
+
+#define MAX_DIR_ENTRIES 250
+
+static char *filelist[MAX_DIR_ENTRIES];
+static char *filelist2[MAX_DIR_ENTRIES];
+
+static int openDrive () {
+
+ dvd_fd = open(DVD, O_RDONLY | O_NONBLOCK);
+
+ if (dvd_fd < 0) {
+ printf ("input_dvd: unable to open dvd drive (%s): %s\n", DVD,
+ strerror(errno));
+ return -1;
+ }
+
+ raw_fd = open(RDVD, O_RDONLY | O_NONBLOCK);
+ if (raw_fd < 0) {
+ raw_fd = dvd_fd;
+ }
+ return raw_fd;
+}
+
+static void closeDrive () {
+
+ if (dvd_fd<0)
+ return;
+
+ close (dvd_fd);
+ if (raw_fd!=dvd_fd)
+ close (raw_fd);
+
+ dvd_fd = -1;
+}
+
+/*
+ * try to open dvd and prepare to read >filename<
+ *
+ * returns lbnum on success, 0 otherwise
+ */
+
+static int openDVDFile (char *filename, off_t *size) {
+
+ char str[256];
+ int lbnum;
+
+ xprintf (VERBOSE|INPUT, "input_dvd : openDVDFile >%s<\n",filename);
+
+ if (openDrive() < 0) {
+ printf ("input_dvd: cannot open dvd drive >%s<\n", DVD);
+ return 0;
+ }
+
+ snprintf (str, sizeof(str), "/VIDEO_TS/%s", filename);
+
+ xprintf (VERBOSE|INPUT, "UDFFindFile %s\n",str);
+
+ if (!(lbnum=UDFFindFile(dvd_fd, str, size))) {
+ printf ("input_dvd: cannot open file >%s<\n", filename);
+
+ closeDrive ();
+
+ return 0;
+ }
+
+ lseek (raw_fd, lbnum * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET) ;
+
+ return lbnum;
+}
+
+
+static void input_plugin_init (void) {
+ int i;
+
+ /*
+ * allocate space for directory listing
+ */
+
+ for (i=0; i<MAX_DIR_ENTRIES; i++) {
+ filelist[i] = (char *) malloc (256);
+ filelist2[i] = (char *) malloc (256);
+ }
+}
+
+static int input_plugin_open (const char *mrl) {
+
+ char *filename;
+
+ xprintf (VERBOSE|INPUT, "input dvd : input_plugin_open >%s<\n", mrl);
+
+ /*
+ * do we handle this kind of MRL ?
+ */
+
+ if (strncasecmp (mrl, "dvd://",6))
+ return 0;
+
+ filename = (char *) &mrl[6];
+
+ xprintf (VERBOSE|INPUT, "input dvd : input_plugin_open media type correct. file name is %s\n",
+ filename);
+
+ sscanf (filename, "VTS_%d_%d.VOB", &gVTSMajor, &gVTSMinor);
+
+ file_lbstart = openDVDFile (filename, &file_size) ;
+ file_lbcur = file_lbstart;
+
+ if (!file_lbstart) {
+ fprintf (stderr, "unable to find >%s< on dvd.\n",filename);
+ return 0;
+ }
+
+ file_size_left = file_size;
+
+ return 1 ;
+}
+
+static uint32_t input_plugin_read (char *buf, uint32_t nlen) {
+
+ if (nlen != DVD_VIDEO_LB_LEN) {
+ /*
+ * Hide the error reporting now, demuxer try to read 6 bytes
+ * at STAGE_BY_CONTENT probe stage
+ */
+ fprintf (stderr, "ERROR in input_dvd plugin read: %d bytes "
+ "is not a sector!\n", nlen);
+ return 0;
+ }
+
+ if (file_size_left < nlen)
+ return 0;
+
+ if (read (raw_fd, buf, DVD_VIDEO_LB_LEN)) {
+
+ file_lbcur++;
+ file_size_left -= DVD_VIDEO_LB_LEN;
+
+ return DVD_VIDEO_LB_LEN;
+ } else
+ fprintf (stderr, "read error in input_dvd plugin\n");
+
+ return 0;
+}
+
+static off_t input_plugin_seek (off_t offset, int origin) {
+
+ offset /= DVD_VIDEO_LB_LEN;
+
+ switch (origin) {
+ case SEEK_END:
+ offset = (file_size / DVD_VIDEO_LB_LEN) - offset;
+
+ case SEEK_SET:
+ file_lbcur = file_lbstart + offset;
+ file_size_left = file_size - (offset * DVD_VIDEO_LB_LEN);
+ break;
+ case SEEK_CUR:
+ if (offset) {
+ file_lbcur += offset;
+ file_size_left = file_size - ((file_lbcur - file_lbstart) * DVD_VIDEO_LB_LEN);
+ } else {
+ return (file_lbcur - file_lbstart) * (off_t) DVD_VIDEO_LB_LEN;
+ }
+
+ break;
+ default:
+ fprintf (stderr, "error in input dvd plugin seek:%d is an unknown origin\n"
+ ,origin);
+ }
+
+ return lseek (raw_fd, file_lbcur * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET) - file_lbstart * (off_t) DVD_VIDEO_LB_LEN;
+}
+
+static off_t input_plugin_get_length (void) {
+ return file_size;
+}
+
+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 DVD_VIDEO_LB_LEN;
+}
+
+static int input_plugin_eject (void) {
+ int ret, status;
+ int fd;
+
+ if((fd = open(DVD, O_RDONLY|O_NONBLOCK)) > -1) {
+
+#if defined (__linux__)
+ if((status = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) {
+ switch(status) {
+ case CDS_TRAY_OPEN:
+ if((ret = ioctl(fd, CDROMCLOSETRAY)) != 0) {
+ xprintf(VERBOSE|INPUT, "CDROMCLOSETRAY failed: %s\n", strerror(errno));
+ }
+ break;
+ case CDS_DISC_OK:
+ if((ret = ioctl(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(fd);
+ return 0;
+ }
+
+#elif defined (__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__)
+
+ if (ioctl(fd, CDIOCALLOW) == -1) {
+ perror("ioctl(cdromallow)");
+ } else {
+ if (ioctl(fd, CDIOCEJECT) == -1) {
+ perror("ioctl(cdromeject)");
+ }
+ }
+
+#endif
+
+ close(fd);
+ }
+ return 1;
+}
+
+static void input_plugin_close (void) {
+ closeDrive ();
+}
+
+static char *input_plugin_get_identifier (void) {
+ return "DVD";
+}
+
+static char** input_plugin_get_dir (char *filename, int *nEntries) {
+
+ int i, fd;
+
+ if (filename) {
+ *nEntries = 0;
+ return NULL;
+ }
+
+ if((fd = open(DVD, O_RDONLY|O_NONBLOCK)) > -1) {
+
+ int nFiles, nFiles2;
+
+ UDFListDir (fd, "/VIDEO_TS", MAX_DIR_ENTRIES, filelist, &nFiles);
+
+ nFiles2 = 0;
+ for (i=0; i<nFiles; i++) {
+ int nLen;
+
+ nLen = strlen (filelist[i]);
+
+ if (nLen<4)
+ continue;
+
+ if (!strcasecmp (&filelist[i][nLen-4], ".VOB")) {
+
+ sprintf (filelist2[nFiles2], "dvd://%s",filelist[i]);
+
+ nFiles2++;
+ }
+
+ }
+
+ *nEntries = nFiles2;
+
+ close (fd);
+
+ } else {
+ *nEntries = 0;
+ return NULL;
+ }
+
+ return filelist2;
+}
+
+static char **input_plugin_get_autoplay_list (int *nFiles) {
+
+ int i, fd;
+
+ if((fd = open(DVD, O_RDONLY|O_NONBLOCK)) > -1) {
+ int nFiles3, nFiles2;
+
+ UDFListDir (fd, "/VIDEO_TS", MAX_DIR_ENTRIES, filelist, &nFiles3);
+
+ nFiles2 = 0;
+ for (i=0; i<nFiles3; i++) {
+ int nLen;
+
+ nLen = strlen (filelist[i]);
+
+ if (nLen<4)
+ continue;
+
+ if (!strcasecmp (&filelist[i][nLen-4], ".VOB")) {
+
+ sprintf (filelist2[nFiles2], "dvd://%s",filelist[i]);
+
+ nFiles2++;
+ }
+
+ }
+
+ *nFiles = nFiles2;
+
+ close (fd);
+
+ } else {
+ *nFiles = 0;
+ return NULL;
+ }
+
+ return filelist2;
+}
+
+static int input_plugin_is_branch_possible (const char *next_mrl) {
+
+ char *filename;
+ int vts_minor, vts_major;
+
+ printf ("input_dvd: is_branch_possible to %s ?\n", next_mrl);
+
+ /*
+ * do we handle this kind of MRL ?
+ */
+
+ if (strncmp (next_mrl, "dvd://",6))
+ return 0;
+
+ filename = (char *) &next_mrl[6];
+
+ if (sscanf (filename, "VTS_%d_%d.VOB", &vts_major, &vts_minor) == 2) {
+ if ((vts_major==gVTSMajor) && (vts_minor==(gVTSMinor+1))) {
+ printf ("input_dvd: branching is possible\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+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,
+ input_plugin_get_dir,
+ 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;
+}
diff --git a/src/input/input_file.c b/src/input/input_file.c
new file mode 100644
index 000000000..d2591d2bd
--- /dev/null
+++ b/src/input/input_file.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2000 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_file.c,v 1.1 2001/04/18 22:34:04 f1rmb Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "xine.h"
+#include "monitor.h"
+#include "input_plugin.h"
+
+
+static uint32_t xine_debug;
+static int input_file_handle;
+static char *input_file_mrl;
+
+static uint32_t file_plugin_get_capabilities () {
+ return INPUT_CAP_SEEKABLE;
+}
+
+static int file_plugin_open (char *mrl) {
+
+ char *filename;
+
+ input_file_mrl = mrl;
+
+ if (!strncasecmp (mrl, "file:",5))
+ filename = &mrl[5];
+ else
+ filename = mrl;
+
+ xprintf (VERBOSE|INPUT, "Opening >%s<\n",filename);
+
+ input_file_handle = open (filename, O_RDONLY);
+
+ if (input_file_handle == -1) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static off_t file_plugin_read (char *buf, off_t len) {
+ return read (input_file_handle, buf, len);
+}
+
+static buf_element_t *file_plugin_read_block (fifo_buffer_t *fifo, off_t todo) {
+
+ off_t num_bytes, total_bytes;
+ buf_element_t *buf = fifo->buffer_pool_alloc ();
+
+ buf->content = buf->mem;
+ total_bytes = 0;
+
+ while (total_bytes < todo) {
+ num_bytes = read (input_file_handle, buf->mem + total_bytes, todo-total_bytes);
+ total_bytes += num_bytes;
+ if (!num_bytes) {
+ buf->free_buffer (buf);
+ return NULL;
+ }
+ }
+
+ return buf;
+}
+
+
+static off_t file_plugin_seek (off_t offset, int origin) {
+ return lseek (input_file_handle, offset, origin);
+}
+
+
+static off_t file_plugin_get_current_pos (){
+ return lseek (input_file_handle, 0, SEEK_CUR);
+}
+
+
+static off_t file_plugin_get_length (void) {
+ struct stat buf ;
+
+ if (fstat (input_file_handle, &buf) == 0) {
+ return buf.st_size;
+ } else
+ perror ("system call fstat");
+ return 0;
+}
+
+static uint32_t file_plugin_get_blocksize () {
+ return 0;
+}
+
+static char **file_plugin_get_dir (char *filename, int *nFiles) {
+ /* not yet implemented */
+
+ printf ("input_file : get_dir () not implemented yet!\n");
+
+ return NULL;
+}
+
+static int file_plugin_eject_media () {
+ return 1; /* doesn't make sense */
+}
+
+static char* file_plugin_get_mrl () {
+ return input_file_mrl;
+}
+
+static void file_plugin_close (void) {
+ xprintf (VERBOSE|INPUT, "closing input\n");
+
+ close(input_file_handle);
+ input_file_handle = -1;
+}
+
+
+static char *file_plugin_get_description (void) {
+ return "plain file input plugin as shipped with xine";
+}
+
+
+static char *file_plugin_get_identifier (void) {
+ return "file";
+}
+
+
+static input_plugin_t plugin_info = {
+ INPUT_INTERFACE_VERSION,
+ file_plugin_get_capabilities,
+ file_plugin_open,
+ file_plugin_read,
+ file_plugin_read_block,
+ file_plugin_seek,
+ file_plugin_get_current_pos,
+ file_plugin_get_length,
+ file_plugin_get_blocksize,
+ file_plugin_get_dir,
+ file_plugin_eject_media,
+ file_plugin_get_mrl,
+ file_plugin_close,
+ file_plugin_get_description,
+ file_plugin_get_identifier,
+ NULL, /* autoplay */
+ NULL /* clut */
+};
+
+
+input_plugin_t *get_input_plugin (int iface, config_values_t *config) {
+
+ /* FIXME: set debug level (from config?) */
+
+ switch (iface) {
+ case 1:
+ input_file_handle = -1;
+ return &plugin_info;
+ break;
+ default:
+ fprintf(stderr,
+ "File input plugin doesn't support plugin API version %d.\n"
+ "PLUGIN DISABLED.\n"
+ "This means there's a version mismatch between xine and this input"
+ "plugin.\nInstalling current input plugins should help.\n",
+ iface);
+ return NULL;
+ }
+}
diff --git a/src/input/input_net.c b/src/input/input_net.c
new file mode 100644
index 000000000..67684320e
--- /dev/null
+++ b/src/input/input_net.c
@@ -0,0 +1,224 @@
+/*
+ * 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
+ *
+ * Read from a tcp network stream over a lan (put a tweaked mp1e encoder the
+ * other end and you can watch tv anywhere in the house ..)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include "xine.h"
+#include "monitor.h"
+#include "input_plugin.h"
+
+static uint32_t xine_debug;
+
+static int input_file_handle;
+
+static int host_connect_attempt(struct in_addr ia, int port) {
+ int s=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ struct sockaddr_in sin;
+
+ fd_set wfd;
+ struct timeval tv;
+
+ if(s==-1)
+ {
+ perror("socket");
+ return -1;
+ }
+
+ if(fcntl(s, F_SETFL, FNDELAY)==-1)
+ {
+ perror("nonblocking");
+ close(s);
+ return -1;
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr = ia;
+ sin.sin_port = htons(port);
+
+ if(connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 && errno != EINPROGRESS)
+ {
+ perror("connect");
+ close(s);
+ return -1;
+ }
+
+ tv.tv_sec = 60; /* We use 60 second timeouts for now */
+ tv.tv_usec = 0;
+
+ FD_ZERO(&wfd);
+ FD_SET(s, &wfd);
+
+ switch(select(s+1, NULL, &wfd, NULL, &tv))
+ {
+ case 0:
+ /* Time out */
+ close(s);
+ return -1;
+ case -1:
+ /* Ermm.. ?? */
+ perror("select");
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+static int host_connect(const char *host, int port) {
+ struct hostent *h;
+ int i;
+ int s;
+
+ h=gethostbyname(host);
+ if(h==NULL)
+ {
+ fprintf(stderr,"unable to resolve '%s'.\n", host);
+ return -1;
+ }
+
+
+ for(i=0; h->h_addr_list[i]; i++)
+ {
+ struct in_addr ia;
+ memcpy(&ia, h->h_addr_list[i],4);
+ s=host_connect_attempt(ia, port);
+ if(s != -1)
+ return s;
+ }
+ fprintf(stderr, "unable to connect to '%s'.\n", host);
+ return -1;
+}
+
+static void input_plugin_init (void) {
+ input_file_handle = -1;
+}
+
+static int input_plugin_open (const char *mrl) {
+
+ char *filename;
+ char *pptr;
+ int port = 7658;
+
+ if (!strncasecmp (mrl, "tcp:",4))
+ filename = (char *) &mrl[4];
+ else
+ return 0;
+
+ if(strncmp(filename, "//", 2)==0)
+ filename+=2;
+
+ xprintf (VERBOSE|INPUT, "Opening >%s<\n", filename);
+
+ pptr=strrchr(filename, ':');
+ if(pptr)
+ {
+ *pptr++=0;
+ sscanf(pptr,"%d", &port);
+ }
+
+ input_file_handle = host_connect(filename, port);
+
+ if (input_file_handle == -1) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static uint32_t input_plugin_read (char *buf, uint32_t nlen) {
+ return read (input_file_handle, buf, nlen);
+}
+
+static off_t input_plugin_seek (off_t offset, int origin) {
+
+ return -1;
+}
+
+static off_t input_plugin_get_length (void) {
+ return 0;
+}
+
+static uint32_t input_plugin_get_capabilities (void) {
+ return 0;
+}
+
+static uint32_t input_plugin_get_blocksize (void) {
+ return 2324;
+}
+
+static int input_plugin_eject (void) {
+ return 1;
+}
+
+static void input_plugin_close (void) {
+ close(input_file_handle);
+ input_file_handle = -1;
+}
+
+static char *input_plugin_get_identifier (void) {
+ return "TCP";
+}
+
+static int input_plugin_is_branch_possible (const char *next_mrl) {
+ return 0;
+}
+
+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,
+ NULL,
+ input_plugin_is_branch_possible,
+ NULL
+};
+
+input_plugin_t *input_plugin_getinfo(uint32_t dbglvl) {
+
+ xine_debug = dbglvl;
+
+ return &plugin_op;
+}
diff --git a/src/input/input_plugin.h b/src/input/input_plugin.h
new file mode 100644
index 000000000..002c1b650
--- /dev/null
+++ b/src/input/input_plugin.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2000 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_plugin.h,v 1.1 2001/04/18 22:34:05 f1rmb Exp $
+ */
+
+#ifndef HAVE_INPUT_PLUGIN_H
+#define HAVE_INPUT_PLUGIN_H
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include "buffer.h"
+#include "configfile.h"
+
+#define INPUT_INTERFACE_VERSION 1
+
+#ifndef CLUT_T
+#define CLUT_T
+typedef struct { /* CLUT == Color LookUp Table */
+ uint8_t foo : 8; /* UNKNOWN: 0x00? */
+ uint8_t y : 8;
+ uint8_t cr : 8;
+ uint8_t cb : 8;
+} __attribute__ ((packed)) clut_t;
+#endif
+
+typedef struct input_plugin_s
+{
+
+ /*
+ * plugin interface version, lower versions _may_ be supported
+ */
+ int interface_version;
+
+ /*
+ * return capabilities of input source
+ */
+
+ uint32_t (*get_capabilities) (void);
+
+ /*
+ * open input MRL - return 1 if succ
+ */
+ int (*open) (char *mrl);
+
+
+ /*
+ * read nlen bytes, return number of bytes read
+ */
+ off_t (*read) (char *buf, off_t nlen);
+
+
+ /*
+ * read one block, return newly allocated block (or NULL on failure)
+ * for blocked input sources len must be == blocksize
+ * the fifo parameter is only used to get access to the buffer_pool_alloc function
+ */
+ buf_element_t *(*read_block)(fifo_buffer_t *fifo, off_t len);
+
+
+ /*
+ * seek position, return new position
+ *
+ * if seeking failed, -1 is returned
+ */
+ off_t (*seek) (off_t offset, int origin);
+
+
+ /*
+ * get current position in stream.
+ *
+ */
+ off_t (*get_current_pos) (void);
+
+
+ /*
+ * return length of input (-1 => unlimited, e.g. stream)
+ */
+ off_t (*get_length) (void);
+
+
+ /*
+ * return block size of input source (if supported, 0 otherwise)
+ */
+
+ uint32_t (*get_blocksize) (void);
+
+
+ /*
+ * ls function
+ * return value: NULL => filename is a file, **char=> filename is a dir
+ */
+ char** (*get_dir) (char *filename, int *nFiles);
+
+
+ /*
+ * eject/load the media (if it's possible)
+ *
+ * returns 0 for temporary failures
+ */
+ int (*eject_media) (void);
+
+
+ /*
+ * return current MRL
+ */
+ char * (*get_mrl) (void);
+
+
+ /*
+ * close input source
+ */
+ void (*close) (void);
+
+
+ /*
+ * return human readable (verbose = 1 line) description for this plugin
+ */
+ char* (*get_description) (void);
+
+
+ /*
+ * return short, human readable identifier for this plugin
+ * this is used for GUI buttons, The identifier must have max. 4 characters
+ * characters (max. 5 including terminating \0)
+ */
+ char* (*get_identifier) (void);
+
+
+ /*
+ * generate autoplay list
+ * return value: list of MRLs
+ */
+ char** (*get_autoplay_list) (int *nFiles);
+
+
+ /*
+ * gets the subtitle/menu palette
+ */
+ clut_t* (*get_clut) (void);
+
+
+} input_plugin_t;
+
+#define INPUT_CAP_SEEKABLE 1
+#define INPUT_CAP_BLOCK 2
+#define INPUT_CAP_AUTOPLAY 4
+#define INPUT_CAP_CLUT 8
+
+
+/*
+ * init/get plugin structure
+ *
+ * try to initialize the plugin with given interface version
+ * and configuration options
+ */
+input_plugin_t *get_input_plugin (int requested_interface,
+ config_values_t *config);
+
+
+
+#endif
diff --git a/src/input/input_rtp.c b/src/input/input_rtp.c
new file mode 100644
index 000000000..88fa5784d
--- /dev/null
+++ b/src/input/input_rtp.c
@@ -0,0 +1,384 @@
+/*
+ * 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
+ *
+ * Xine input plugin for multicast video streams.
+ *
+ *
+ * This is something of an experiment - it doesn't work well yet. Originally
+ * the intent was to read an rtp stream, from, for example, Cisco IP
+ * Tv. That's still a long term goal but RTP doesn't fit well in an input
+ * plugin because typically video is carried on one multicast group and audio
+ * in another - i.e it's already demultiplexed and an input plugin would
+ * actually have to reassemble the content. Now that demultiplexers are
+ * becomming separate loadable objects the right thing to do is to write an
+ * RTP demux plugin and a playlist plugin that handles SDP.
+ *
+ *
+ * In the meantime some experience with multicast video was wanted. Not
+ * having hardware available to construct a stream on the fly a server was
+ * written to multicast the contents of an mpeg program stream - it just
+ * reads a pack then transmits it at the appropriate time as follows.
+ *
+ * fd is open for read on mpeg stream, sock for write on a multicast socket.
+ *
+ * while (1) {
+ * /* read pack */
+ * read(fd, buf, 2048)
+ * /* end of stream */
+ * if (buf[3] == 0xb9)
+ * return 0;
+ *
+ * /* extract the system reference clock, srcb, from the pack */
+ *
+ * send_at = srcb/90000.0;
+ * while (time_now < send_at) {
+ * wait;
+ * }
+ * r = write(sock, buf, 2048);
+ * }
+ *
+ * One problem is that a stream from a DVD needs each pack sending
+ * at approx 2.5ms intervals which is a shorter interval than the
+ * standard linux clock. The RTC can be used for more finely grained
+ * timing.
+ *
+ * If you live in a non multicast friendly environment then the stream
+ * can be unicast.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+#include "input_plugin.h"
+
+static int last_input_error;
+static int input_eof;
+static uint32_t xine_debug;
+
+typedef struct _input_buffer {
+ struct _input_buffer *next;
+ unsigned char *buf;
+} input_buffer;
+
+#define N_BUFFERS 128
+#define IBUFFER_SIZE 2048
+
+static int input_file_handle = -1;
+
+input_buffer *free_buffers;
+input_buffer **fifo_head;
+input_buffer fifo_tail;
+
+pthread_mutex_t buffer_mutex;
+pthread_cond_t buffer_notempty;
+
+static pthread_t reader_thread;
+
+static void * input_plugin_read_loop(void *);
+
+static int host_connect_attempt(struct in_addr ia, int port) {
+ int s=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ struct sockaddr_in sin;
+
+ if(s==-1) {
+ perror("socket");
+ return -1;
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr = ia;
+ sin.sin_port = htons(port);
+
+ /* datagram socket */
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin))) {
+ perror("bind failed");
+ exit(1);
+ }
+ /* multicast ? */
+ if ((ntohl(sin.sin_addr.s_addr) >> 28) == 0xe) {
+ struct ip_mreqn mreqn;
+
+ mreqn.imr_multiaddr.s_addr = sin.sin_addr.s_addr;
+ mreqn.imr_address.s_addr = INADDR_ANY;
+ mreqn.imr_ifindex = 0;
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreqn,sizeof(mreqn))) {
+ perror("setsockopt IP_ADD_MEMBERSHIP failed (multicast kernel?)");
+ exit(1);
+ }
+ }
+
+ return s;
+}
+
+static int host_connect(const char *host, int port) {
+ struct hostent *h;
+ int i;
+ int s;
+
+ h=gethostbyname(host);
+ if(h==NULL)
+ {
+ fprintf(stderr,"unable to resolve '%s'.\n", host);
+ return -1;
+ }
+
+
+ for(i=0; h->h_addr_list[i]; i++)
+ {
+ struct in_addr ia;
+ memcpy(&ia, h->h_addr_list[i],4);
+ s=host_connect_attempt(ia, port);
+ if(s != -1)
+ return s;
+ }
+ fprintf(stderr, "unable to connect to '%s'.\n", host);
+ return -1;
+}
+
+static void input_plugin_init (void) {
+ int bufn;
+
+ for (bufn = 0; bufn < N_BUFFERS; bufn++) {
+ input_buffer *buf = malloc(sizeof(input_buffer));
+ if (!buf) {
+ fprintf(stderr, "unable to allocate input buffer.\n");
+ exit(1);
+ }
+ buf->buf = malloc(IBUFFER_SIZE);
+ if (!buf->buf) {
+ fprintf(stderr, "unable to allocate input buffer.\n");
+ exit(1);
+ }
+ buf->next = free_buffers;
+ free_buffers = buf;
+ }
+}
+
+static int input_plugin_open (char *mrl) {
+ char *filename;
+ char *pptr;
+ int port = 7658;
+ pthread_attr_t thread_attrs;
+
+ if (!strncmp (mrl, "rtp:",4)) {
+ filename = &mrl[4];
+ } else if (!strncmp (mrl, "udp:",4)) {
+ filename = &mrl[4];
+ } else
+ return 0;
+
+ if(strncmp(filename, "//", 2)==0)
+ filename+=2;
+
+ printf ("Opening >%s<\n", filename);
+
+ pptr=strrchr(filename, ':');
+ if(pptr)
+ {
+ *pptr++=0;
+ sscanf(pptr,"%d", &port);
+ }
+
+ if (input_file_handle != -1)
+ close(input_file_handle);
+ input_file_handle = host_connect(filename, port);
+
+ if (input_file_handle == -1) {
+ return 0;
+ }
+
+ last_input_error = 0;
+ input_eof = 0;
+ fifo_tail.next = &fifo_tail;
+ fifo_head = &fifo_tail.next;
+
+ pthread_cond_init(&buffer_notempty, NULL);
+ pthread_attr_init(&thread_attrs);
+ pthread_attr_setdetachstate(&thread_attrs, PTHREAD_CREATE_DETACHED);
+ pthread_create(&reader_thread, &thread_attrs, input_plugin_read_loop, (void *)input_file_handle);
+ pthread_attr_destroy(&thread_attrs);
+
+ return 1;
+}
+
+static uint32_t input_plugin_read (char *buf, uint32_t nlen) {
+ input_buffer *ibuf;
+
+ pthread_mutex_lock (&buffer_mutex);
+ while (fifo_tail.next == &fifo_tail) {
+ if (input_eof) {
+ pthread_mutex_unlock (&buffer_mutex);
+ return 0;
+ }
+ if (last_input_error) {
+ pthread_mutex_unlock (&buffer_mutex);
+ return last_input_error;
+ }
+ pthread_cond_wait(&buffer_notempty, &buffer_mutex);
+ }
+ ibuf = fifo_tail.next;
+ fifo_tail.next = fifo_tail.next->next;
+
+ /* Is FIFO now empty */
+ if (fifo_tail.next == &fifo_tail)
+ fifo_head = &fifo_tail.next;
+
+ pthread_mutex_unlock (&buffer_mutex);
+
+ memcpy(buf, ibuf->buf, nlen < IBUFFER_SIZE ? nlen : IBUFFER_SIZE);
+
+ pthread_mutex_lock (&buffer_mutex);
+ ibuf->next = free_buffers;
+ free_buffers = ibuf;
+ pthread_mutex_unlock (&buffer_mutex);
+
+ return nlen < IBUFFER_SIZE ? nlen : IBUFFER_SIZE;
+}
+
+static void * input_plugin_read_loop(void *arg) {
+ int inf = (int) arg;
+ input_buffer *buf;
+ int r;
+ unsigned short seq = 0;
+ static int warned = 0;
+
+ char whirly[] = "/-\\|";
+ int gig = 0;
+
+ while (1) {
+ pthread_mutex_lock (&buffer_mutex);
+ /* we expect to be able to get a free buffer - possibly we
+ could be a bit more reasonable but this will do for now. */
+ if (!free_buffers) {
+ input_eof = 1;
+ if (!warned) {
+ printf("OUCH - ran out of buffers\n");
+ warned = 1;
+ }
+ pthread_cond_signal(&buffer_notempty);
+ continue;
+ }
+ warned = 0;
+ buf = free_buffers;
+ free_buffers = free_buffers->next;
+ pthread_mutex_unlock (&buffer_mutex);
+
+ /* printf("%c\r", whirly[(gig++ % 4)]); */
+ /* fflush(stdout); */
+ r = read(inf, buf->buf, IBUFFER_SIZE);
+ if (r < 0) {
+ /* descriptor may be closed by main thread */
+ if (r != EBADF)
+ last_input_error = r;
+ return 0;
+ }
+ if (r == 0) {
+ input_eof = 1;
+ return 0;
+ }
+
+ /* For now - check whether we're dropping input */
+ if (++seq != *(unsigned short *)buf->buf) {
+ printf("OUCH - dropped input packet %d %d\n", seq, *(unsigned short *)buf->buf);
+ seq = *(unsigned short *)buf->buf;
+ }
+ buf->buf[1] = buf->buf[0] = 0;
+ pthread_mutex_lock (&buffer_mutex);
+ buf->next = *fifo_head;
+ *fifo_head = buf;
+ fifo_head = &buf->next;
+ pthread_cond_signal(&buffer_notempty);
+ pthread_mutex_unlock (&buffer_mutex);
+ }
+}
+
+static off_t input_plugin_seek (off_t offset, int origin) {
+
+ return -1;
+}
+
+static uint32_t input_plugin_get_length (void) {
+ return 0;
+}
+
+static uint32_t input_plugin_get_capabilities (void) {
+ return 0;
+}
+
+static uint32_t input_plugin_get_blocksize (void) {
+ return 2048;
+}
+
+static void input_plugin_close (void) {
+ close(input_file_handle);
+ input_file_handle = -1;
+}
+
+static int input_plugin_eject (void) {
+ return 1;
+}
+
+static char *input_plugin_get_identifier (void) {
+ return "RTP";
+}
+
+static int input_plugin_is_branch_possible (char *next_mrl) {
+ return 0;
+}
+
+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,
+ NULL,
+ input_plugin_is_branch_possible,
+ NULL
+};
+
+input_plugin_t *input_plugin_getinfo(uint32_t dbglvl) {
+
+ xine_debug = dbglvl;
+
+ return &plugin_op;
+}
diff --git a/src/input/input_stdin_fifo.c b/src/input/input_stdin_fifo.c
new file mode 100644
index 000000000..488564795
--- /dev/null
+++ b/src/input/input_stdin_fifo.c
@@ -0,0 +1,219 @@
+/*
+ * 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_stdin_fifo.c,v 1.1 2001/04/18 22:34:05 f1rmb Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "input_plugin.h"
+
+static uint32_t xine_debug;
+
+static int input_file_handle;
+
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static void input_plugin_init(void) {
+
+ input_file_handle = -1;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static int input_plugin_open(const char *mrl) {
+ char *filename;
+ char *pfn;
+
+ if(!strncasecmp(mrl, "stdin:", 6)
+ || !strncmp(mrl, "-", 1)) {
+ filename = "/dev/stdin";
+ }
+ else if(!strncasecmp(mrl, "fifo:", 5)) {
+
+ if((pfn = strrchr((mrl+5), ':')) != NULL) {
+ filename = ++pfn;
+ }
+ else {
+ filename = (char *) &mrl[5];
+ }
+
+ }
+ else {
+ filename = (char *) mrl;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "%s(%d): opening >%s< file\n",
+ __FILE__, __LINE__, filename);
+#endif
+
+ input_file_handle = open(filename, O_RDONLY);
+
+ if(input_file_handle == -1) {
+ return 0;
+ }
+
+ return 1;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static uint32_t input_plugin_read(char *buf, uint32_t nlen) {
+
+ int n, nBytesRead;
+
+ nBytesRead = 0;
+
+ while (nBytesRead < nlen) {
+ n = read(input_file_handle, &buf[nBytesRead], nlen-nBytesRead);
+
+ if (n<0)
+ return n;
+ else if (!n)
+ return nBytesRead;
+
+ nBytesRead += n;
+ }
+ return nBytesRead;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static off_t input_plugin_seek(off_t offset, int origin) {
+
+ return lseek(input_file_handle, offset, origin);
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static off_t input_plugin_get_length(void) {
+ struct stat buf ;
+
+ if(fstat(input_file_handle, &buf) == 0) {
+ return buf.st_size;
+ }
+ else {
+ fprintf(stderr, "%s(%d): fstat() failed: %s\n",
+ __FILE__, __LINE__, strerror(errno));
+ }
+
+ return 0;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static uint32_t input_plugin_get_capabilities(void) {
+
+ return 0;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static uint32_t input_plugin_get_blocksize(void) {
+
+ return 0;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static int input_plugin_eject (void) {
+ return 1;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static void input_plugin_close(void) {
+
+#ifdef DEBUG
+ fprintf(stderr, "%s(%d): closing input\n",
+ __FILE__, __LINE__);
+#endif
+
+ close(input_file_handle);
+ input_file_handle = -1;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static char *input_plugin_get_identifier(void) {
+
+ return "stdin_fifo";
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static int input_plugin_is_branch_possible (const char *next_mrl) {
+
+ return 0;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+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,
+ NULL,
+ input_plugin_is_branch_possible,
+ NULL
+};
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+input_plugin_t *input_plugin_getinfo(uint32_t dbglvl) {
+
+ xine_debug = dbglvl;
+
+ return &plugin_op;
+}
+/* ------------------------------------------------------------------------- */
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;
+}
+
+