diff options
author | Daniel Caujolle-Bert <f1rmb@users.sourceforge.net> | 2001-04-18 22:33:39 +0000 |
---|---|---|
committer | Daniel Caujolle-Bert <f1rmb@users.sourceforge.net> | 2001-04-18 22:33:39 +0000 |
commit | db6b7c2e1c52c536a7f9690a410bf69817e0b2c5 (patch) | |
tree | fdaf7537abca3d4875ad21322c54888914ed15a2 /src/input | |
download | xine-lib-db6b7c2e1c52c536a7f9690a410bf69817e0b2c5.tar.gz xine-lib-db6b7c2e1c52c536a7f9690a410bf69817e0b2c5.tar.bz2 |
Initial revision
CVS patchset: 1
CVS date: 2001/04/18 22:33:39
Diffstat (limited to 'src/input')
-rw-r--r-- | src/input/Makefile.am | 72 | ||||
-rw-r--r-- | src/input/dvd_udf.c | 661 | ||||
-rw-r--r-- | src/input/dvd_udf.h | 39 | ||||
-rw-r--r-- | src/input/input_dvd.c | 438 | ||||
-rw-r--r-- | src/input/input_file.c | 191 | ||||
-rw-r--r-- | src/input/input_net.c | 224 | ||||
-rw-r--r-- | src/input/input_plugin.h | 178 | ||||
-rw-r--r-- | src/input/input_rtp.c | 384 | ||||
-rw-r--r-- | src/input/input_stdin_fifo.c | 219 | ||||
-rw-r--r-- | src/input/input_vcd.c | 586 |
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; +} + + |