diff options
Diffstat (limited to 'src/input/input_dvd.c')
-rw-r--r-- | src/input/input_dvd.c | 1841 |
1 files changed, 1184 insertions, 657 deletions
diff --git a/src/input/input_dvd.c b/src/input/input_dvd.c index 6e87dc282..d37734d89 100644 --- a/src/input/input_dvd.c +++ b/src/input/input_dvd.c @@ -1,5 +1,6 @@ -/* - * Copyright (C) 2000-2001 the xine project +/* + * Copyright (C) 2000, 2001 the xine project, + * Rich Wareham <richwareham@users.sourceforge.net> * * This file is part of xine, a unix video player. * @@ -7,871 +8,1397 @@ * 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.52 2002/07/05 17:32:01 mroi Exp $ + * $Id: input_dvd.c,v 1.53 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +/* This file was origninally part of the xine-dvdnav project + * at http://dvd.sf.net/. + */ + +/* TODO: + * + * - Proper internationalisation of strings. + * - Failure dialogue. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include <dlfcn.h> +/* Standard includes */ #include <stdio.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <errno.h> -#include <unistd.h> #include <stdlib.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> #include <string.h> -#include <inttypes.h> - -#ifdef HAVE_SYS_CDIO_H -# include <sys/cdio.h> -#endif -#ifdef HAVE_LINUX_CDROM_H -# include <linux/cdrom.h> -#elif defined __FreeBSD__ -# include "sys/dvdio.h" -#endif -#if ! defined (HAVE_LINUX_CDROM_H) && ! defined (HAVE_SYS_CDIO_H) -#error "you need to add dvd support for your platform to input_dvd.c and configure.in" -#endif +#include <errno.h> -#include "xine_internal.h" -#include "xineutils.h" -#include "input_plugin.h" -#include "dvd_udf.h" -#include "read_cache.h" +#include <sys/mount.h> +#include <sys/wait.h> -extern int errno; +#include <sys/poll.h> +#include <sys/ioctl.h> -#ifdef __GNUC__ -#define LOG_MSG_STDERR(xine, message, args...) { \ - xine_log(xine, XINE_LOG_MSG, message, ##args); \ - fprintf(stderr, message, ##args); \ - } -#define LOG_MSG(xine, message, args...) { \ - xine_log(xine, XINE_LOG_MSG, message, ##args); \ - printf(message, ##args); \ - } +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) +#include <sys/dvdio.h> +#include <sys/cdio.h> /* CDIOCALLOW etc... */ +#elif defined(__linux__) +#include <linux/cdrom.h> #else -#define LOG_MSG_STDERR(xine, ...) { \ - xine_log(xine, XINE_LOG_MSG, __VA_ARGS__); \ - fprintf(stderr, __VA_ARGS__); \ - } -#define LOG_MSG(xine, ...) { \ - xine_log(xine, XINE_LOG_MSG, __VA_ARGS__); \ - printf(__VA_ARGS__); \ - } +#error "Need the DVD ioctls" #endif +/* Xine includes */ +#include <xine/xineutils.h> +#include <xine/buffer.h> +#include <xine/input_plugin.h> +#include <xine/video_out.h> +#include <xine/events.h> +#include <xine/metronom.h> +#include <xine/spu_decoder_api.h> +#include <xine/xine_internal.h> + +/* DVDNAV includes */ +#include <dvdnav.h> + +/* libdvdread includes */ +#include <dvdread/nav_read.h> + +/* Print debug messages? */ +/* #define INPUT_DEBUG 1 */ + +/* Print trace messages? */ +/* #define INPUT_DEBUG_TRACE */ + +/* Print debug of eject */ +/* #define LOG_DVD_EJECT */ + +/* Current play mode (title only or menus?) */ +#define MODE_NAVIGATE 0 +#define MODE_TITLE 1 + +/* Is seeking enabled? 1 - Yes, 0 - No */ +#define CAN_SEEK 1 + +/* The default DVD device on Solaris is not /dev/dvd */ #if defined(__sun) -#define RDVD "/vol/dev/aliases/cdrom0" -#define DVD RDVD +#define DVD_PATH "/vol/dev/aliases/cdrom0" #else -#define DVD "/dev/dvd" -#define RDVD "/dev/rdvd" +#define DVD_PATH "/dev/dvd" +#endif + +/* Some misc. defines */ +#define DVD_BLOCK_SIZE 2048 +#ifndef BUF_DEMUX_BLOCK +#define BUF_DEMUX_BLOCK 0x05000000 #endif +#define VIDEO_FILL_THROTTLE 5 -typedef struct { +/* Debugging macros */ +#if INPUT_DEBUG +#define dprint(s, args...) fprintf(stderr, __FUNCTION__ ": " s, ##args); +#else +#define dprint(s, args...) /* Nowt */ +#endif - input_plugin_t input_plugin; +#if INPUT_DEBUG_TRACE +#define trace_print(s, args...) fprintf(stdout, __FUNCTION__ ": " s, ##args); +#else +#define trace_print(s, args...) /* Nothing */ +#endif - xine_t *xine; - - char *mrl; - config_values_t *config; - - int dvd_fd; - int raw_fd; - read_cache_t *read_cache; - off_t file_size; - off_t file_size_left; - int file_lbstart; - int file_lbcur; - int gVTSMinor; - int gVTSMajor; - - const char *device; - const char *raw_device; +/* Globals */ +extern int errno; - /* - * udf dir function - */ +/* Array to hold MRLs returned by get_autoplay_list */ #define MAX_DIR_ENTRIES 250 - - char *filelist[MAX_DIR_ENTRIES]; - char *filelist2[MAX_DIR_ENTRIES]; - - int mrls_allocated_entries; - mrl_t **mrls; +#define MAX_STR_LEN 255 +char filelist[MAX_DIR_ENTRIES][MAX_STR_LEN]; +char *filelist2[MAX_DIR_ENTRIES]; -} dvd_input_plugin_t; +/* A Temporary string (FIXME: May cause problems if multiple + * dvdnavs in multiple threads). */ +char temp_str[256]; +#define TEMP_STR_LEN 255 +typedef struct { + input_plugin_t input_plugin; /* Parent input plugin type */ + int pause_timer; /* Cell stil-time timer */ + int pause_counter; + time_t pause_end_time; + int32_t buttonN; -/* ***************************************************************** */ -/* Private functions */ -/* ***************************************************************** */ -/* - * Callbacks for configuratoin changes. - */ -static void device_change_cb(void *data, cfg_entry_t *cfg) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) data; + /* Flags */ + int opened; /* 1 if the DVD device is already open */ - this->device = cfg->str_value; -} - -static void rawdevice_change_cb(void *data, cfg_entry_t *cfg) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) data; + /* Xine specific variables */ + config_values_t *config; /* Pointer to XineRC config file */ + char *dvd_device; /* Default DVD device */ + char *current_dvd_device; /* DVD device currently open */ + char *mrl; /* Current MRL */ + int mode; + dvdnav_t *dvdnav; /* Handle for libdvdnav */ + xine_t *xine; + char dvd_name[128]; + size_t dvd_name_length; + mrl_t **mrls; + int num_mrls; - this->raw_device = cfg->str_value; -} - -static int openDrive (dvd_input_plugin_t *this) { + /* special buffer handling for libdvdnav caching */ + pthread_mutex_t buf_mutex; + void *source; + void (*free_buffer)(buf_element_t *); + int mem_stack; + unsigned char *mem[1024]; +} dvdnav_input_plugin_t; + +static void flush_buffers(dvdnav_input_plugin_t *this); +static void xine_dvdnav_send_button_update(dvdnav_input_plugin_t *this, int mode); + +/* Callback on device name change */ +static void device_change_cb(void *data, cfg_entry_t *cfg) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) data; - this->dvd_fd = open(this->device, O_RDONLY /* | O_NONBLOCK */ ); - - if (this->dvd_fd < 0) { - LOG_MSG(this->xine, _("input_dvd: unable to open dvd drive (%s): %s\n"), - this->device, strerror(errno)); - return -1; - } - - this->raw_fd = open(this->raw_device, O_RDONLY /* | O_NONBLOCK */ ); - if (this->raw_fd < 0) { - this->raw_fd = this->dvd_fd; - } + this->dvd_device = cfg->str_value; +} - read_cache_set_fd (this->read_cache, this->raw_fd); +static uint32_t dvdnav_plugin_get_capabilities (input_plugin_t *this_gen) { + trace_print("Called\n"); - return this->raw_fd; + return INPUT_CAP_AUTOPLAY | INPUT_CAP_BLOCK | INPUT_CAP_CLUT | +#if CAN_SEEK + INPUT_CAP_SEEKABLE | INPUT_CAP_VARIABLE_BITRATE | +#endif + INPUT_CAP_AUDIOLANG | INPUT_CAP_SPULANG | INPUT_CAP_GET_DIR | INPUT_CAP_CHAPTERS; } -static void closeDrive (dvd_input_plugin_t *this) { +void read_ahead_cb(void *this_gen, cfg_entry_t *entry) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; - if (this->dvd_fd < 0) - return; - - close (this->dvd_fd); - if (this->raw_fd != this->dvd_fd) - close (this->raw_fd); + if(!this) + return; - this->dvd_fd = -1; + if(!this->dvdnav) + return; + dvdnav_set_readahead_flag(this->dvdnav, entry->num_value); } + +void region_changed_cb(void *this_gen, cfg_entry_t *entry) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; -#ifdef __sun -#include <sys/scsi/generic/commands.h> -#include <sys/scsi/impl/uscsi.h> -#include <sys/stat.h> - -/* SCSI mmc3 DVD Commands */ -#define GPCMD_READ_DVD_STRUCTURE 0xad -#define GPCMD_SEND_DVD_STRUCTURE 0xad -#define GPCMD_REPORT_KEY 0xa4 -#define GPCMD_SEND_KEY 0xa3 + if(!this) + return; -/* DVD struct types */ -#define DVD_STRUCT_PHYSICAL 0x00 -#define DVD_STRUCT_COPYRIGHT 0x01 -#define DVD_STRUCT_DISCKEY 0x02 -#define DVD_STRUCT_BCA 0x03 -#define DVD_STRUCT_MANUFACT 0x04 + if(!this->dvdnav) + return; -struct dvd_copyright { - uint8_t type; + if((entry->num_value >= 1) && (entry->num_value <= 8)) { + /* FIXME: Remove debug message */ + dprint("Setting region code to %i (0x%x)\n", + entry->num_value, 1<<(entry->num_value-1)); + dvdnav_set_region_mask(this->dvdnav, 1<<(entry->num_value-1)); + } +} - uint8_t layer_num; - uint8_t cpst; - uint8_t rmi; -}; +void language_changed_cb(void *this_gen, cfg_entry_t *entry) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; -typedef union { - uint8_t type; + if(!this) + return; -/* - struct dvd_physical physical; -*/ - struct dvd_copyright copyright; -/* - struct dvd_disckey disckey; - struct dvd_bca bca; - struct dvd_manufact manufact; -*/ -} dvd_struct; + if(!this->dvdnav) + return; + dvdnav_menu_language_select(this->dvdnav, entry->str_value); + dvdnav_audio_language_select(this->dvdnav, entry->str_value); + dvdnav_spu_language_select(this->dvdnav, entry->str_value); +} + +void update_title_display(dvdnav_input_plugin_t *this) { + xine_ui_event_t uevent; + int tt=-1, pr=-1; + size_t temp_str_length=0; -/* - * Read DVD "Copyright Structure" from DVD Drive - */ -static int -dvd_read_copyright(dvd_input_plugin_t *this, dvd_struct *s) -{ - struct uscsi_cmd sc; - union scsi_cdb rs_cdb; - uint8_t buf[8]; - - memset(&rs_cdb, 0, sizeof(rs_cdb)); - rs_cdb.scc_cmd = GPCMD_READ_DVD_STRUCTURE; - rs_cdb.cdb_opaque[6] = s->copyright.layer_num; - rs_cdb.cdb_opaque[7] = s->type; - rs_cdb.cdb_opaque[8] = (sizeof(buf) >> 8) & 0xff; - rs_cdb.cdb_opaque[9] = sizeof(buf) & 0xff; - - memset(&sc, 0, sizeof(sc)); - sc.uscsi_cdb = (caddr_t)&rs_cdb; - sc.uscsi_cdblen = 12; - sc.uscsi_bufaddr = buf; - sc.uscsi_buflen = sizeof(buf); - sc.uscsi_flags = USCSI_ISOLATE|USCSI_READ; - sc.uscsi_timeout = 15; + if(!this || !(this->xine)) + return; - memset(buf, 0, sizeof(buf)); + /* Set title/chapter display */ + uevent.event.type = XINE_EVENT_UI_SET_TITLE; + uevent.data = temp_str; - if (ioctl(this->raw_fd, USCSICMD, &sc)) { - LOG_MSG(this->xine, _("USCSICMD dvd_read_copyright: %s"), strerror(errno)); - return -1; + dvdnav_current_title_info(this->dvdnav, &tt, &pr); + + if(tt != -1) { + int num_angle = 0, cur_angle = 0; + /* no menu here */ + /* Reflect angle info if appropriate */ + dvdnav_get_angle_info(this->dvdnav, &cur_angle, &num_angle); + if(num_angle > 1) { + snprintf(temp_str, TEMP_STR_LEN, + "Title %i, Chapter %i, Angle %i of %i", + tt,pr,cur_angle, num_angle); + } else { + snprintf(temp_str, TEMP_STR_LEN, + "Title %i, Chapter %i", + tt,pr); + } + } else { + strcpy(temp_str, "DVD Navigator: Menu"); } - if (sc.uscsi_status) { - LOG_MSG_STDERR(this->xine, _("bad status: READ DVD STRUCTURE (copyright)\n")); - return -1; + temp_str_length = strlen(temp_str); + + if (this->dvd_name[0] != 0 && (temp_str_length + this->dvd_name_length < TEMP_STR_LEN)) { + snprintf(temp_str+temp_str_length, TEMP_STR_LEN - temp_str_length, + ", %s", + &this->dvd_name[0]); } + + dprint("Changing title to read '%s'\n", temp_str); + xine_send_event(this->xine, &uevent.event); +} - s->copyright.cpst = buf[4]; - s->copyright.rmi = buf[5]; +static void dvdnav_plugin_stop (input_plugin_t *this_gen) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*) this_gen; + fprintf(stderr, "dvdnav_plugin_stop called.\n"); + if (this->dvdnav) { + fprintf(stderr, "Now get out of still.\n"); + dvdnav_still_skip(this->dvdnav); + } +} - return 0; +static void dvdnav_plugin_close (input_plugin_t *this_gen) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; + + trace_print("Called\n"); + + if(this->opened) + dvdnav_close(this->dvdnav); + this->dvdnav = NULL; + this->opened = 0; + this->dvd_name[0] = 0; + this->dvd_name_length = 0; } +static void dvdnav_build_mrl_list(dvdnav_input_plugin_t *this) { + int num_titles, *num_parts; -/* - * Check the environment, if we're running under sun's - * vold/rmmount control. - */ -static void -check_solaris_vold_device(dvd_input_plugin_t *this) -{ - char *volume_device; - char *volume_name; - char *volume_action; - char *device; - struct stat stb; + /* skip DVD if already open */ + if (this->opened) return; + if (this->mrls) { + free(this->mrls); + this->mrls = NULL; + this->num_mrls = 0; + } - if ((volume_device = getenv("VOLUME_DEVICE")) != NULL && - (volume_name = getenv("VOLUME_NAME")) != NULL && - (volume_action = getenv("VOLUME_ACTION")) != NULL && - strcmp(volume_action, "insert") == 0) { + if (dvdnav_open(&(this->dvdnav), + this->current_dvd_device) == DVDNAV_STATUS_ERR) + return; + + dvdnav_get_number_of_titles(this->dvdnav, &num_titles); + if ((num_parts = (int *) calloc(num_titles, sizeof(int)))) { + int num_mrls = 1, i; + /* for each title, count the number of programs */ + for (i = 1; i <= num_titles; i++) { + num_parts[i-1] = 0; + dvdnav_title_play(this->dvdnav, i); + dvdnav_get_number_of_programs(this->dvdnav, &num_parts[i-1]); + num_mrls += num_parts[i-1]; /* num_mrls = total number of programs */ + } - device = malloc(strlen(volume_device) + strlen(volume_name) + 2); - if (device == NULL) - return; - sprintf(device, "%s/%s", volume_device, volume_name); - if (stat(device, &stb) != 0 || !S_ISCHR(stb.st_mode)) { - free(device); - return; + /* allocate enough memory for: + * - a list of pointers to mrls sizeof(mrl_t *) * num_mrls + 1 + * - an array of mrl structures sizeof(mrl_t) * num_mrls + * - enough chars for every filename sizeof(char)*25 * num_mrls + * - "dvd://:000000.000000\0" = 25 chars + */ + if ((this->mrls = (mrl_t **) malloc(sizeof(mrl_t *) + num_mrls * + (sizeof(mrl_t*) + sizeof(mrl_t) + 25*sizeof(char))))) { + + /* the first mrl struct comes after the pointer list */ + mrl_t *mrl = (mrl_t *) &this->mrls[num_mrls+1]; + /* the chars for filenames come after the mrl structs */ + char *name = (char *) &mrl[num_mrls]; + int pos = 0, j; + this->num_mrls = num_mrls; + + for (i = 1; i <= num_titles; i++) { + for (j = (i == 1 ? 0 : 1); j <= num_parts[i-1]; j++) { + this->mrls[pos++] = mrl; + mrl->origin = NULL; + mrl->mrl = name; + mrl->link = NULL; + mrl->type = mrl_dvd; + mrl->size = 0; + snprintf(name, 25, (j == 0) ? "dvd://" : + (j == 1) ? "dvd://:%d" : + "dvd://:%d.%d", i, j); + name = &name[25]; + mrl++; + } + } + this->mrls[pos] = NULL; /* terminate list */ } - this->device = this->raw_device = device; + free(num_parts); } + + /* Reset the VM so that we don't break anything */ + /* dvdnav_reset(this->dvdnav); */ + + dvdnav_close(this->dvdnav); } -#endif /* - * try to open dvd and prepare to read >filename< + * Opens the DVD plugin. The MRL takes the following form: + * + * dvd://[dvd_path][:vts[.program]] * - * returns lbnum on success, 0 otherwise + * e.g. + * dvd:// - Play (navigate) /dev/dvd + * dvd:///dev/dvd2 - Play (navigate) /dev/dvd2 + * dvd:///dev/dvd2:1 - Play Title 1 from /dev/dvd2 + * dvd://:1.3 - Play Title 1, program 3 from /dev/dvd */ -static int openDVDFile (dvd_input_plugin_t *this, - char *filename, off_t *size) { - char str[256]; - int lbnum; - int encrypted=0; - - if (openDrive(this) < 0) { - LOG_MSG(this->xine, _("input_dvd: cannot open dvd drive >%s<\n"), this->device); +static int dvdnav_plugin_open (input_plugin_t *this_gen, char *mrl) { + char *locator; + int colon_point; + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen; + dvdnav_status_t ret; + char *intended_dvd_device; + cfg_entry_t *region_entry, *lang_entry, *cache_entry; + + trace_print("Called\n"); + /* printf("open1: dvdnav=%p opened=%d\n",this->dvdnav, this->opened); */ + + this->mrl = mrl; + this->pause_timer = 0; + this->dvd_name[0] = 0; + this->dvd_name_length = 0; + + /* Check we can handle this MRL */ + if (!strncasecmp (mrl, "dvd://",9)) + locator = &mrl[9]; + else { return 0; } -#if defined HAVE_LINUX_CDROM_H - { - dvd_struct dvd; - - dvd.copyright.type = DVD_STRUCT_COPYRIGHT; - dvd.copyright.layer_num = 0; - if (ioctl (this->dvd_fd, DVD_READ_STRUCT, &dvd) < 0) { - LOG_MSG(this->xine, _("input_dvd: Could not read Copyright Structure\n")); - return 0; - } - encrypted = (dvd.copyright.cpst != 0) ; + /* Attempt to parse MRL */ + colon_point=0; + while((locator[colon_point] != '\0') && (locator[colon_point] != ':')) { + colon_point++; } -#elif defined __FreeBSD__ - { - struct dvd_struct dvd; - dvd.format = DVD_STRUCT_COPYRIGHT; - dvd.layer_num = 0; + if(locator[colon_point] == ':') { + this->mode = MODE_TITLE; + } else { + this->mode = MODE_NAVIGATE; + } - if (ioctl(this->dvd_fd, DVDIOCREADSTRUCTURE, &dvd) < 0) { - LOG_MSG(this->xine, _("input_dvd: Could not read Copyright Structure\n")); + locator[colon_point] = '\0'; + ret = DVDNAV_STATUS_OK; + if(colon_point == 0) { + /* Use default device */ + intended_dvd_device=this->dvd_device; + } else { + /* Use specified device */ + intended_dvd_device=locator; + } + + if(this->opened) { + if ( intended_dvd_device==this->current_dvd_device ) { + /* Already open, so skip opening */ + } else { + /* Changing DVD device */ + dvdnav_close(this->dvdnav); + this->dvdnav=NULL; + this->opened=0; + ret = dvdnav_open(&this->dvdnav, intended_dvd_device); + if(ret == DVDNAV_STATUS_ERR) { + fprintf(stderr, "Error opening DVD device\n"); + return 0; + } + this->opened=1; + this->current_dvd_device=intended_dvd_device; + } + } else { + ret = dvdnav_open(&this->dvdnav, intended_dvd_device); + if(ret == DVDNAV_STATUS_ERR) { + fprintf(stderr, "Error opening DVD device\n"); return 0; } - - encrypted = (dvd.cpst != 0); + this->opened=1; + this->current_dvd_device=intended_dvd_device; } -#elif defined __sun - { - dvd_struct dvd; - - dvd.copyright.type = DVD_STRUCT_COPYRIGHT; - dvd.copyright.layer_num = 0; - if (dvd_read_copyright(this, &dvd) < 0) { - LOG_MSG(this->xine, _("input_dvd: Could not read Copyright Structure.\n" - " Assuming disk is not encrypted.\n")); - } else - encrypted = (dvd.copyright.cpst != 0); + if (1) { + int fd, i; + off64_t off; + uint8_t data[DVD_VIDEO_LB_LEN]; + + /* Read DVD name */ + fd=open(intended_dvd_device, O_RDONLY); + if (fd > 0) { + off = lseek64( fd, 32 * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET ); + if( off == ( 32 * (int64_t) DVD_VIDEO_LB_LEN ) ) { + off = read( fd, data, DVD_VIDEO_LB_LEN ); + close(fd); + if (off == ( (int64_t) DVD_VIDEO_LB_LEN )) { + fprintf( stderr, "DVD Title: "); + for(i=25; i < 73; i++ ) { + if((data[i] == 0)) break; + if((data[i] > 32) && (data[i] < 127)) { + fprintf(stderr, "%c", data[i]); + } else { + fprintf(stderr, " "); + } + } + strncpy(&this->dvd_name[0], &data[25], 48); + /* fprintf(stderr, "TITLE:%s\n",&this->dvd_name[0]); */ + this->dvd_name[48]=0; + this->dvd_name_length=strlen(&this->dvd_name[0]); + fprintf( stderr, "\nDVD Serial Number: "); + for(i=73; i < 89; i++ ) { + if((data[i] == 0)) break; + if((data[i] > 32) && (data[i] < 127)) { + fprintf(stderr, "%c", data[i]); + } else { + fprintf(stderr, " "); + } + } + fprintf( stderr, "\nDVD Title (Alternative): "); + for(i=89; i < 128; i++ ) { + if((data[i] == 0)) break; + if((data[i] > 32) && (data[i] < 127)) { + fprintf(stderr, "%c", data[i]); + } else { + fprintf(stderr, " "); + } + } + fprintf( stderr, "\n"); + } else { + fprintf( stderr, "libdvdread: Can't read name block. Probably not a DVD-ROM device.\n"); + } + } else { + fprintf( stderr, "libdvdread: Can't seek to block %u\n", 32 ); + } + } else { + fprintf(stderr,"NAME OPEN FAILED\n"); + } } -#endif - if( encrypted ) { - LOG_MSG(this->xine, - _("\ninput_dvd: Sorry, this plugin doesn't play encrypted DVDs. The legal status\n" - " of CSS decryption is unclear and we can't provide such code.\n" - " Please check http://dvd.sf.net for more information.\n")); - return 0; + + /* Set region code */ + region_entry = this->config->lookup_entry(this->config, + "input.dvd_region"); + if(region_entry) { + region_changed_cb(this, region_entry); } - - snprintf (str, sizeof(str), "/VIDEO_TS/%s", filename); - - if (!(lbnum = UDFFindFile(this->dvd_fd, str, size))) { - LOG_MSG(this->xine, _("input_dvd: cannot open file >%s<\n"), filename); - - closeDrive (this); - - return 0; + + /* Set languages */ + lang_entry = this->config->lookup_entry(this->config, + "input.dvdnav_language"); + if(lang_entry) { + language_changed_cb(this, lang_entry); } + + /* Set cache usage */ + cache_entry = this->config->lookup_entry(this->config, + "input.dvdnav_use_readahead"); + if(cache_entry) { + read_ahead_cb(this, cache_entry); + } + + if(this->mode == MODE_TITLE) { + int tt, i, pr, found; + int titles; + + /* A program and/or VTS was specified */ + locator += colon_point + 1; - lseek (this->raw_fd, lbnum * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET) ; - - return lbnum; -} -/* ***************************************************************** */ -/* END OF PRIVATES */ -/* ***************************************************************** */ - -/* - * - */ -static uint32_t dvd_plugin_get_capabilities (input_plugin_t *this) { - return INPUT_CAP_SEEKABLE | INPUT_CAP_PREVIEW | INPUT_CAP_BLOCK | INPUT_CAP_AUTOPLAY | INPUT_CAP_GET_DIR; -} - -/* - * - */ -static int dvd_plugin_open (input_plugin_t *this_gen, char *mrl) { - char *filename; - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - - this->mrl = mrl; - - /* - * do we handle this kind of MRL ? - */ - if (strncasecmp (mrl, "dvd://", 6)) - return 0; + if(locator[0] == '\0') { + /* Empty specifier */ + fprintf(stderr, "Incorrect MRL format.\n"); + dvdnav_close(this->dvdnav); + return 0; + } - filename = (char *) &mrl[6]; + /* See if there is a period. */ + found = -1; + for(i=0; i<strlen(locator); i++) { + if(locator[i] == '.') { + found = i; + locator[i] = '\0'; + } + } + tt = strtol(locator, NULL,10); - sscanf (filename, "VTS_%d_%d.VOB", &this->gVTSMajor, &this->gVTSMinor); + dvdnav_get_number_of_titles(this->dvdnav, &titles); + if((tt <= 0) || (tt > titles)) { + fprintf(stderr, "Title %i is out of range (1 to %i).\n", tt, + titles); + dvdnav_close(this->dvdnav); + return 0; + } - this->file_lbstart = openDVDFile (this, filename, &this->file_size) ; - this->file_lbcur = this->file_lbstart; + /* If there was a program specified, get that too. */ + pr = -1; + if(found != -1) { + pr = strtol(locator+found+1, NULL,10); + } - if (!this->file_lbstart) { - LOG_MSG(this->xine, _("input_dvd: Unable to find >%s< on dvd.\n"), filename); - return 0; + dprint("Jumping to VTS >%i<, prog >%i<\n", tt, pr); + if(pr != -1) { + dvdnav_part_play(this->dvdnav, tt, pr); + } else { + dvdnav_title_play(this->dvdnav, tt); + } } - - this->file_size_left = this->file_size; - - return 1 ; -} - -static int dvd_plugin_is_branch_possible(input_plugin_t *this_gen, char *nextmrl ) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - char *mrl; - - if (strncasecmp (nextmrl, "dvd://", 6)) - return 0; - - mrl = this->mrl; - mrl += 6; - nextmrl += 6; + dprint("DVD device successfully opened.\n"); - if( strncasecmp (mrl, "VTS_", 4) || strncasecmp (nextmrl, "VTS_", 4) ) - return 0; - return 1; -} - -static off_t dvd_plugin_read (input_plugin_t *this_gen, - char *buf, off_t nlen) { - - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - int bytes_read; +} - if (nlen != DVD_VIDEO_LB_LEN) { +static void dvdnav_plugin_free_buffer(buf_element_t *buf) { + dvdnav_input_plugin_t *this = buf->source; + + pthread_mutex_lock(&this->buf_mutex); + /* give this buffer back to libdvdnav */ + dvdnav_free_cache_block(this->dvdnav, buf->mem); + /* reconstruct the original xine buffer */ + buf->free_buffer = this->free_buffer; + buf->source = this->source; + buf->mem = this->mem[--this->mem_stack]; + pthread_mutex_unlock(&this->buf_mutex); + /* give this buffer back to xine's pool */ + buf->free_buffer(buf); +} - LOG_MSG(this->xine, _("input_dvd: error read: %Ld bytes is not a sector!\n"), - nlen); +static buf_element_t *dvdnav_plugin_read_block (input_plugin_t *this_gen, + fifo_buffer_t *fifo, off_t nlen) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; + buf_element_t *buf; + dvdnav_status_t result; + int event, len; + int finished = 0; + unsigned char *block; - return 0; + if(fifo == NULL) { + dprint("values of \\beta will give rise to dom!\n"); + return NULL; } - if (this->file_size_left < nlen) - return 0; + /* Read buffer */ + buf = fifo->buffer_pool_alloc (fifo); + block = buf->mem; - bytes_read = read (this->raw_fd, buf, DVD_VIDEO_LB_LEN); - if (bytes_read == DVD_VIDEO_LB_LEN) { + while(!finished) { + if (block != buf->mem) { + /* if we already have a dvdnav cache block, give it back first */ + dvdnav_free_cache_block(this->dvdnav, block); + block = buf->mem; + } + result = dvdnav_get_next_cache_block (this->dvdnav, &block, &event, &len); + if(result == DVDNAV_STATUS_ERR) { + fprintf(stderr, "Error getting next block from DVD (%s)\n", + dvdnav_err_to_string(this->dvdnav)); + if (block != buf->mem) dvdnav_free_cache_block(this->dvdnav, block); + buf->free_buffer(buf); + return NULL; + } - this->file_lbcur++; - this->file_size_left -= DVD_VIDEO_LB_LEN; + switch(event) { + case DVDNAV_BLOCK_OK: + { + buf->content = block; + buf->type = BUF_DEMUX_BLOCK; - return DVD_VIDEO_LB_LEN; - } else if (bytes_read < 0) { - LOG_MSG(this->xine, _("input_dvd: read error in input_dvd plugin (%s)\n"), - strerror (errno)); + /* Make sure we don't think we are still paused */ + this->pause_timer = 0; + + finished = 1; + } + break; + case DVDNAV_NOP: + { + /* Nothing */ + } + break; + case DVDNAV_STILL_FRAME: + { + + /* OK, So xine no-longer accepts BUF_VIDEO_FILLs, find out + * how else we provide the hint + */ + dvdnav_still_event_t *still_event = + (dvdnav_still_event_t*)(block); + buf->type = BUF_CONTROL_NOP; + finished = 1; + + /* Xine's method of doing still-frames */ + if (this->pause_timer == 0) { + dprint("dvd:input_dvdnav.c:Stillframe! (pause time = 0x%02x)\n", + still_event->length); + this->pause_timer = still_event->length; + this->pause_end_time = time(NULL) + this->pause_timer; + this->pause_counter = 0; + break; + } + + if(this->pause_timer == 0xff) { + this->pause_counter++; + xine_usec_sleep(100000); + break; + } + if ( (this->pause_timer != 0xFF) && + (time(NULL) >= this->pause_end_time) ){ + this->pause_timer = 0; + this->pause_end_time = 0; + dvdnav_still_skip(this->dvdnav); + break; + } + if(this->pause_timer) { + this->pause_counter++; + dprint("dvd:input_dvdnav.c:Stillframe! (pause_timer = 0x%02x) counter=%d\n", + still_event->length, this->pause_counter); + xine_usec_sleep(100000); + break; + } + } + break; + case DVDNAV_SPU_STREAM_CHANGE: + { + dvdnav_spu_stream_change_event_t *stream_event = + (dvdnav_spu_stream_change_event_t*) (block); + buf->content = block; + buf->type = BUF_CONTROL_SPU_CHANNEL; + buf->decoder_info[0] = stream_event->physical_wide; + buf->decoder_info[1] = stream_event->physical_letterbox; + buf->decoder_info[2] = stream_event->physical_pan_scan; + dprint("SPU stream wide %d, letterbox %d, pan&scan %d\n", + stream_event->physical_wide, + stream_event->physical_letterbox, + stream_event->physical_pan_scan); + finished = 1; + } + break; + case DVDNAV_AUDIO_STREAM_CHANGE: + { + dvdnav_audio_stream_change_event_t *stream_event = + (dvdnav_audio_stream_change_event_t*) (block); + buf->content = block; + buf->type = BUF_CONTROL_AUDIO_CHANNEL; + buf->decoder_info[0] = stream_event->physical; + dprint("AUDIO stream %d\n", stream_event->physical); + finished = 1; + } + break; + case DVDNAV_HIGHLIGHT: + { + xine_dvdnav_send_button_update(this, 0); + } + break; + case DVDNAV_VTS_CHANGE: + { + int aspect, permission; + + dprint("VTS change\n"); + + /* Check for video aspect change and scaling permissions */ + aspect = dvdnav_get_video_aspect(this->dvdnav); + permission = dvdnav_get_video_scale_permission(this->dvdnav); + + buf->type = BUF_VIDEO_MPEG; + buf->decoder_flags = BUF_FLAG_SPECIAL; + buf->decoder_info[1] = BUF_SPECIAL_ASPECT; + buf->decoder_info[2] = aspect; + buf->decoder_info[3] = permission; + finished = 1; + } + break; + case DVDNAV_CELL_CHANGE: + { + xine_ui_event_t uevent; + + /* Tell Xine to update the UI */ + uevent.event.type = XINE_EVENT_UI_CHANNELS_CHANGED; + uevent.data = NULL; + xine_send_event(this->xine, &uevent.event); + + update_title_display(this); + } + break; + case DVDNAV_SEEK_DONE: + { + dprint("Seek done\n"); + /* FIXME: This should send a message to clear all currently displaying subtitle. */ + } + break; + case DVDNAV_HOP_CHANNEL: + { + flush_buffers(this); + break; + } + case DVDNAV_NAV_PACKET: + { + buf->content = block; + buf->type = BUF_DEMUX_BLOCK; + finished = 1; + } + break; + case DVDNAV_SPU_CLUT_CHANGE: + { + buf->content = block; + buf->type = BUF_SPU_CLUT; + finished = 1; + } + break; + case DVDNAV_STOP: + { + if (buf->mem != block) dvdnav_free_cache_block(this->dvdnav, block); + buf->free_buffer(buf); + /* return NULL to indicate end of stream */ + return NULL; + } + default: + dprint("FIXME: Unknown event (%i)\n", event); + break; + } } - else { - LOG_MSG(this->xine, _("input_dvd: short read in input_dvd (%d != %d)\n"), - bytes_read, DVD_VIDEO_LB_LEN); + + if (block != buf->mem) { + /* we have received a buffer from the libdvdnav cache, store all + * necessary values to reconstruct xine's buffer and modify it according to + * our needs. */ + pthread_mutex_lock(&this->buf_mutex); + if (this->mem_stack < 1024) { + this->mem[this->mem_stack++] = buf->mem; + this->free_buffer = buf->free_buffer; + this->source = buf->source; + buf->mem = block; + buf->free_buffer = dvdnav_plugin_free_buffer; + buf->source = this; + } else { + /* the stack for storing the memory chunks from xine is full, we cannot + * modify the buffer, because we would not be able to reconstruct it. + * Therefore we copy the data and give the buffer back. */ + dprint("too many buffers issued, memory stack exceeded\n"); + memcpy(buf->mem, block, 2048); + dvdnav_free_cache_block(this->dvdnav, block); + buf->content = buf->mem; + } + pthread_mutex_unlock(&this->buf_mutex); } - return 0; + return buf; } +static off_t dvdnav_plugin_read (input_plugin_t *this_gen, char *ch_buf, off_t len) { +/* dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; */ -static buf_element_t *dvd_plugin_read_block (input_plugin_t *this_gen, - fifo_buffer_t *fifo, off_t nlen) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - buf_element_t *buf; - - if (nlen != DVD_VIDEO_LB_LEN || this->file_size_left < nlen) { - /* - * Hide the error reporting now, demuxer try to read 6 bytes - * at STAGE_BY_CONTENT probe stage - */ - if(nlen != DVD_VIDEO_LB_LEN) - LOG_MSG(this->xine, - _("input_dvd: error in input_dvd plugin read: %Ld bytes " - "is not a sector!\n"), nlen); - return NULL; - } - - if ((buf = read_cache_read_block (this->read_cache, (off_t)this->file_lbcur*DVD_VIDEO_LB_LEN))) { + /* FIXME: Implement somehow */ - this->file_lbcur++; - this->file_size_left -= DVD_VIDEO_LB_LEN; - buf->type = BUF_DEMUX_BLOCK; + return 0; +} + +static off_t dvdnav_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; + + trace_print("Called\n"); - } else { - LOG_MSG(this->xine, _("input_dvd: read error in input_dvd plugin\n")); + if(!this || !this->dvdnav) { + return -1; } + + return dvdnav_sector_search(this->dvdnav, offset / DVD_BLOCK_SIZE , origin) * DVD_BLOCK_SIZE; - - return buf; + return -1; } +static off_t dvdnav_plugin_get_current_pos (input_plugin_t *this_gen){ + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; + uint32_t pos=0; + uint32_t length=1; + dvdnav_status_t result; + trace_print("Called\n"); -static off_t dvd_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - - offset /= DVD_VIDEO_LB_LEN; - - switch (origin) { - case SEEK_END: - offset = (this->file_size / DVD_VIDEO_LB_LEN) - offset; - - case SEEK_SET: - this->file_lbcur = this->file_lbstart + offset; - this->file_size_left = this->file_size - (offset * DVD_VIDEO_LB_LEN); - break; - case SEEK_CUR: - if (offset) { - this->file_lbcur += offset; - this->file_size_left = this->file_size - - ((this->file_lbcur - this->file_lbstart) * DVD_VIDEO_LB_LEN); - } else { - return (this->file_lbcur - this->file_lbstart) * - (off_t) DVD_VIDEO_LB_LEN; - } - - break; - default: - LOG_MSG(this->xine, _("input_dvd: seek: %d is an unknown origin\n"), origin); + if(!this || !this->dvdnav) { + return 0; } - - return lseek (this->raw_fd, - this->file_lbcur * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET) - - this->file_lbstart * (off_t) DVD_VIDEO_LB_LEN; + result = dvdnav_get_position(this->dvdnav, &pos, &length); + return (off_t)pos * (off_t)2048; } +static off_t dvdnav_plugin_get_length (input_plugin_t *this_gen) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; + uint32_t pos=0; + uint32_t length=1; + dvdnav_status_t result; + + trace_print("Called\n"); -static off_t dvd_plugin_get_current_pos (input_plugin_t *this_gen){ - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; + if(!this || !this->dvdnav) { + return 0; + } - return ((this->file_lbcur - this->file_lbstart) * DVD_VIDEO_LB_LEN); + result = dvdnav_get_position(this->dvdnav, &pos, &length); + return (off_t)length * (off_t)2048; } +static uint32_t dvdnav_plugin_get_blocksize (input_plugin_t *this_gen) { + trace_print("Called\n"); -static off_t dvd_plugin_get_length (input_plugin_t *this_gen) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - - return this->file_size; + return DVD_BLOCK_SIZE; } +static mrl_t **dvdnav_plugin_get_dir (input_plugin_t *this_gen, + char *filename, int *nFiles) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; -static uint32_t dvd_plugin_get_blocksize (input_plugin_t *this_gen) { + trace_print("Called\n"); + if (filename) { *nFiles = 0; return NULL; } - return DVD_VIDEO_LB_LEN; + dvdnav_build_mrl_list((dvdnav_input_plugin_t *) this_gen); + *nFiles = this->num_mrls; + return this->mrls; } +static int dvdnav_umount_media(char *device) +{ + char *argv[10]; + int i; + pid_t pid; + int status; + argv[0]="umount"; + argv[1]=device; + argv[2]=0; + pid=fork(); + if (pid == 0) { + i= execv("/bin/umount", argv); + exit(127); + } + do { + if(waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) + return -1; + } + else { + return WEXITSTATUS(status); + } + } while(1); + + return -1; +} + -static int dvd_plugin_eject_media (input_plugin_t *this_gen) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; +static int dvdnav_plugin_eject_media (input_plugin_t *this_gen) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen; int ret, status; int fd; - if((fd = open(this->device, O_RDONLY|O_NONBLOCK)) > -1) { + /* printf("dvd:Eject Device %s current device %s opened=%d handle=%p trying...\n",this->dvd_device, this->current_dvd_device, this->opened, this->dvdnav); */ + dvdnav_plugin_close (this_gen) ; + ret=dvdnav_umount_media(this->current_dvd_device); + /********** + printf ("umount result: %s\n", + strerror(errno)); + ***********/ + if ((fd = open (this->current_dvd_device, O_RDONLY|O_NONBLOCK)) > -1) { -#if defined (HAVE_LINUX_CDROM_H) +#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) { - LOG_MSG(this->xine, _("input_dvd: CDROMCLOSETRAY failed: %s\n"), - strerror(errno)); - } - break; + if((ret = ioctl(fd, CDROMCLOSETRAY)) != 0) { +#ifdef LOG_DVD_EJECT + dprint ("CDROMCLOSETRAY failed: %s\n", + strerror(errno)); +#endif + } + break; case CDS_DISC_OK: - if((ret = ioctl(fd, CDROMEJECT)) != 0) { - LOG_MSG(this->xine, _("input_dvd: CDROMEJECT failed: %s\n"), strerror(errno)); - } - break; + if((ret = ioctl(fd, CDROMEJECT)) != 0) { +#ifdef LOG_DVD_EJECT + dprint ("CDROMEJECT failed: %s\n", strerror(errno)); +#endif + } + break; } } else { - LOG_MSG(this->xine, _("input_dvd: CDROM_DRIVE_STATUS failed: %s\n"), - strerror(errno)); +#ifdef LOG_DVD_EJECT + dprint ("CDROM_DRIVE_STATUS failed: %s\n", + strerror(errno)); +#endif close(fd); return 0; } +#elif defined (__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__) -#elif defined (HAVE_CDIO_H) - -# if defined (__sun) - status = 0; - if ((ret = ioctl(fd, CDROMEJECT)) != 0) { - LOG_MSG(this->xine, _("input_dvd: CDROMEJECT failed: %s\n"), strerror(errno)); - } - -# else if (ioctl(fd, CDIOCALLOW) == -1) { - LOG_MSG(this->xine, _("ioctl(cdromallow): %s"), strerror(errno)); + perror("ioctl(cdromallow)"); } else { if (ioctl(fd, CDIOCEJECT) == -1) { - LOG_MSG(this->xine, _("ioctl(cdromeject): %s"), strerror(errno)); + perror("ioctl(cdromeject)"); } } -# endif #endif close(fd); + } else { + dprint("Device %s failed to open during eject calls\n",this->current_dvd_device); } return 1; } +static char* dvdnav_plugin_get_mrl (input_plugin_t *this_gen) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; + + trace_print("Called\n"); -static void dvd_plugin_close (input_plugin_t *this_gen) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - - closeDrive (this); + return this->mrl; } +static char *dvdnav_plugin_get_description (input_plugin_t *this_gen) { + trace_print("Called\n"); -static void dvd_plugin_stop (input_plugin_t *this_gen) { - dvd_plugin_close(this_gen); + return "DVD Navigator"; } +static char *dvdnav_plugin_get_identifier (input_plugin_t *this_gen) { + trace_print("Called\n"); -static char *dvd_plugin_get_description (input_plugin_t *this_gen) { - - return _("dvd device input plugin as shipped with xine"); + return "DVD"; } +static void flush_buffers(dvdnav_input_plugin_t *this) { + /* Small hack for still menus with audio. Thanks to + * the Captain for doing this in d5d. The changes are necessary to + * stop some audio problems (esp. with R2 'Dalekmania'). + */ -static char *dvd_plugin_get_identifier (input_plugin_t *this_gen) { + if (this->xine->audio_fifo) + this->xine->audio_fifo->clear (this->xine->audio_fifo); + + if (this->xine->video_fifo) + this->xine->video_fifo->clear (this->xine->video_fifo); - return "DVD"; + + if (this->xine->cur_audio_decoder_plugin) + this->xine->cur_audio_decoder_plugin->reset(this->xine->cur_audio_decoder_plugin); + if (this->xine->cur_video_decoder_plugin) + this->xine->cur_video_decoder_plugin->flush(this->xine->cur_video_decoder_plugin); } +static void xine_dvdnav_send_button_update(dvdnav_input_plugin_t *this, int mode) { + int button; + spu_button_t spu_button; + xine_spu_event_t spu_event; + dvdnav_get_current_highlight(this->dvdnav, &button); + if (button == this->buttonN && (mode ==0) ) return; + this->buttonN = button; /* Avoid duplicate sending of button info */ + dprint("sending_button_update button=%d mode=%d\n", button, mode); + /* Do we want to show or hide the button? */ + /* libspudec will control hiding */ + spu_event.event.type = XINE_EVENT_SPU_BUTTON; + spu_event.data = &spu_button; + spu_button.show = mode + 1; /* mode=0 select, 1 activate. */ + spu_button.buttonN = button; + xine_send_event(this->xine, &spu_event.event); +} -static mrl_t **dvd_plugin_get_dir (input_plugin_t *this_gen, - char *filename, int *nEntries) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - int i, fd; +static void dvdnav_event_listener (void *this_gen, xine_event_t *event) { - *nEntries = 0; - - if (filename) - return NULL; - - if((fd = open(this->device, O_RDONLY /* | O_NONBLOCK */ )) > -1) { - int nFiles, nFiles2; - - UDFListDir (fd, "/VIDEO_TS", MAX_DIR_ENTRIES, this->filelist, &nFiles); + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen; - nFiles2 = 0; - for (i=0; i<nFiles; i++) { - int nLen; + if(!this->dvdnav) { + return; + } - nLen = strlen (this->filelist[i]); + switch(event->type) { + case XINE_EVENT_INPUT_MENU1: + dvdnav_menu_call(this->dvdnav, DVD_MENU_Root); + break; + case XINE_EVENT_INPUT_MENU2: + dvdnav_menu_call(this->dvdnav, DVD_MENU_Title); + break; + case XINE_EVENT_INPUT_MENU3: + dvdnav_menu_call(this->dvdnav, DVD_MENU_Audio); + break; + case XINE_EVENT_INPUT_NEXT: + dvdnav_next_pg_search(this->dvdnav); + break; + case XINE_EVENT_INPUT_PREVIOUS: + dvdnav_prev_pg_search(this->dvdnav); + break; + case XINE_EVENT_INPUT_ANGLE_NEXT: + { + int num = 0, current = 0; + dvdnav_get_angle_info(this->dvdnav, ¤t, &num); + + if(num != 0) { + current ++; + if(current > num) + current = 1; + } + dvdnav_angle_change(this->dvdnav, current); + dprint("Changing to angle %i\n", current); + + update_title_display(this); + } + break; + case XINE_EVENT_INPUT_ANGLE_PREVIOUS: + { + int num = 0, current = 0; + dvdnav_get_angle_info(this->dvdnav, ¤t, &num); + + if(num != 0) { + current --; + if(current <= 0) + current = num; + } + dvdnav_angle_change(this->dvdnav, current); + dprint("Changing to angle %i\n", current); + + update_title_display(this); + } + break; + case XINE_EVENT_INPUT_SELECT: + { + xine_dvdnav_send_button_update(this, 1); + dvdnav_button_activate(this->dvdnav); + } + break; + case XINE_EVENT_MOUSE_BUTTON: + { + xine_input_event_t *input_event = (xine_input_event_t*) event; + xine_dvdnav_send_button_update(this, 1); + dvdnav_mouse_activate(this->dvdnav, input_event->x, + input_event->y); + } + break; + case XINE_EVENT_INPUT_BUTTON_FORCE: /* For libspudec to feedback forced button select from NAV PCI packets. */ + { + xine_spu_event_t *spu_event = (xine_spu_event_t *) event; + spu_button_t *but = spu_event->data; + fprintf(stderr, "xine_dvd:BUTTON_FORCE %d\n", but->buttonN); + dvdnav_button_select(this->dvdnav, but->buttonN); + } + break; + case XINE_EVENT_MOUSE_MOVE: + { + xine_input_event_t *input_event = (xine_input_event_t*) event; + /* printf("Mouse move (x,y) = (%i,%i)\n", input_event->x, + input_event->y); */ + dvdnav_mouse_select(this->dvdnav, input_event->x, input_event->y); + xine_dvdnav_send_button_update(this, 0); + } + break; + case XINE_EVENT_INPUT_UP: + dvdnav_upper_button_select(this->dvdnav); + xine_dvdnav_send_button_update(this, 0); + break; + case XINE_EVENT_INPUT_DOWN: + dvdnav_lower_button_select(this->dvdnav); + xine_dvdnav_send_button_update(this, 0); + break; + case XINE_EVENT_INPUT_LEFT: + dvdnav_left_button_select(this->dvdnav); + xine_dvdnav_send_button_update(this, 0); + break; + case XINE_EVENT_INPUT_RIGHT: + dvdnav_right_button_select(this->dvdnav); + xine_dvdnav_send_button_update(this, 0); + break; + } + + return; +} - if (nLen<4) - continue; +static int dvdnav_plugin_get_optional_data (input_plugin_t *this_gen, + void *data, int data_type) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen; + + switch(data_type) { - if (!strcasecmp (&this->filelist[i][nLen-4], ".VOB")) { - char str[1024]; + case INPUT_OPTIONAL_DATA_AUDIOLANG: { + uint16_t lang; + int8_t channel; + + /* Be paranoid */ + if(this && this->xine && this->dvdnav) { - if(nFiles2 >= this->mrls_allocated_entries) { - ++this->mrls_allocated_entries; - /* note: 1 extra pointer for terminating NULL */ - this->mrls = realloc(this->mrls, (this->mrls_allocated_entries+1) * sizeof(mrl_t*)); - this->mrls[nFiles2] = (mrl_t *) xine_xmalloc(sizeof(mrl_t)); - } + if(!(dvdnav_is_domain_vts(this->dvdnav))) { + sprintf(data, "%s", "nav"); + goto __audio_success; + } + + channel = (int8_t) xine_get_audio_channel(this->xine); + /* printf("********* AUDIO CHANNEL = %d\n", channel); */ + channel = dvdnav_get_audio_logical_stream(this->dvdnav, channel); + if(channel != -1) { + lang = dvdnav_audio_stream_to_lang(this->dvdnav, channel); - if(this->mrls[nFiles2]->mrl) { - this->mrls[nFiles2]->mrl = (char *) - realloc(this->mrls[nFiles2]->mrl, strlen(this->filelist[i]) + 7); - } + if(lang != 0xffff) { + sprintf(data, " %c%c", lang >> 8, lang & 0xff); + } else { - this->mrls[nFiles2]->mrl = (char *) - xine_xmalloc(strlen(this->filelist[i]) + 7); + sprintf(data, "%3i", xine_get_audio_channel(this->xine)); } + } + else { + channel = xine_get_audio_channel(this->xine); + sprintf(data, "%3i", channel); + } + + __audio_success: + /* printf("********** RETURNING '%s'\n", (char *)data); */ + return INPUT_OPTIONAL_SUCCESS; + } + return INPUT_OPTIONAL_UNSUPPORTED; + } + break; - this->mrls[nFiles2]->origin = NULL; - sprintf(this->mrls[nFiles2]->mrl, "dvd://%s", this->filelist[i]); - this->mrls[nFiles2]->link = NULL; - this->mrls[nFiles2]->type = (0 | mrl_dvd); - /* determine size */ - memset(&str, 0, sizeof(str)); - sprintf (str, "/VIDEO_TS/%s", this->filelist[i]); - UDFFindFile(fd, str, &this->mrls[nFiles2]->size); + case INPUT_OPTIONAL_DATA_SPULANG: { + uint16_t lang; + int8_t channel; + + /* Be paranoid */ + if(this && this->xine && this->dvdnav) { - nFiles2++; + if(!(dvdnav_is_domain_vts(this->dvdnav))) { + sprintf(data, "%3s", "off"); + goto __spu_success; } + channel = (int8_t) xine_get_spu_channel(this->xine); + /* printf("********* SPU CHANNEL = %i\n", channel); */ + if(channel == -1) + channel = dvdnav_get_spu_logical_stream(this->dvdnav, this->xine->spu_channel); + else + channel = dvdnav_get_spu_logical_stream(this->dvdnav, channel); + + if(channel != -1) { + lang = dvdnav_spu_stream_to_lang(this->dvdnav, channel); + + if(lang != 0xffff) { + sprintf(data, " %c%c", lang >> 8, lang & 0xff); + } + else { + sprintf(data, "%3i", xine_get_spu_channel(this->xine)); + } + } + else { + channel = xine_get_spu_channel(this->xine); + if(channel == -1) + sprintf(data, "%3s", "off"); + else + sprintf(data, "%3i", channel); + } + + __spu_success: + /* printf("********** RETURNING '%s'\n", (char *)data); */ + return INPUT_OPTIONAL_SUCCESS; } - - *nEntries = nFiles2; - - close (fd); - - } - else { - LOG_MSG(this->xine, _("input_dvd: unable to open dvd drive (%s): %s\n"), - this->device, strerror(errno)); - return NULL; + return INPUT_OPTIONAL_UNSUPPORTED; } - /* - * Freeing exceeded mrls if exists. - */ - while(this->mrls_allocated_entries > *nEntries) { - MRL_ZERO(this->mrls[this->mrls_allocated_entries - 1]); - free(this->mrls[this->mrls_allocated_entries--]); + break; + } - /* - * This is useful to let UI know where it should stops ;-). - */ - this->mrls[*nEntries] = NULL; - - return this->mrls; + return INPUT_OPTIONAL_UNSUPPORTED; } +static char **dvdnav_plugin_get_autoplay_list (input_plugin_t *this_gen, + int *nFiles) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen; + dvdnav_status_t res; + int titles, i; + trace_print("get_autoplay_list entered\n"); + /* Close the plugin is opened */ + if(this->opened) { + dvdnav_close(this->dvdnav); + this->opened = 0; + } -static char **dvd_plugin_get_autoplay_list (input_plugin_t *this_gen, - int *nFiles) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - int i, fd; - - if((fd = open(this->device, O_RDONLY /* | O_NONBLOCK */ )) > -1) { - int nFiles3, nFiles2; - - UDFListDir (fd, "/VIDEO_TS", MAX_DIR_ENTRIES, this->filelist, &nFiles3); - - nFiles2 = 0; - for (i=0; i<nFiles3; i++) { - int nLen; - - nLen = strlen (this->filelist[i]); - - if (nLen<4) - continue; - - if (!strcasecmp (&this->filelist[i][nLen-4], ".VOB")) { - - if(this->filelist2[nFiles2] == NULL) - this->filelist2[nFiles2] = (char *) realloc(this->filelist2[nFiles2], - sizeof(char *) * 256); - - sprintf (this->filelist2[nFiles2], "dvd://%s", this->filelist[i]); - - nFiles2++; - } - - } - - *nFiles = nFiles2; + /* rebuild thie MRL browser list */ + dvdnav_build_mrl_list(this); - this->filelist2[*nFiles] = (char *) realloc(this->filelist2[*nFiles], sizeof(char *)); - this->filelist2[*nFiles] = NULL; - close (fd); - - } else { - LOG_MSG(this->xine, _("input_dvd: unable to open dvd drive (%s): %s\n"), - this->device, strerror(errno)); - *nFiles = 0; + /* Use default path */ + res = dvdnav_open(&(this->dvdnav), this->current_dvd_device); + if(res == DVDNAV_STATUS_ERR) { return NULL; } - return this->filelist2; -} - + this->opened = 1; + + /* Return a list of all titles */ + snprintf (&(filelist[0][0]), MAX_STR_LEN, "dvd://"); + filelist2[0] = &(filelist[0][0]); -static char* dvd_plugin_get_mrl (input_plugin_t *this_gen) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; + dvdnav_get_number_of_titles(this->dvdnav, &titles); + for(i=1; i<=titles; i++) { + snprintf (&(filelist[i][0]), MAX_STR_LEN, "dvd://:%i", i); + filelist2[i] = &(filelist[i][0]); + } + *nFiles=titles+1; + filelist2[*nFiles] = NULL; + dprint("get_autoplay_list exiting opened=%d dvdnav=%p\n",this->opened, this->dvdnav); - return this->mrl; + return filelist2; } - -static int dvd_plugin_get_optional_data (input_plugin_t *this_gen, - void *data, int data_type) { - /* - switch(data_type) { - - case INPUT_OPTIONAL_DATA_CLUT: - ... - return INPUT_OPTIONAL_SUCCESS; - break; - - case INPUT_OPTIONAL_DATA_AUDIOLANG: - ... - return INPUT_OPTIONAL_SUCCESS; - break; - - } - */ - return INPUT_OPTIONAL_UNSUPPORTED; +void dvdnav_plugin_dispose(input_plugin_t *this_gen) { + dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; + pthread_mutex_destroy(&this->buf_mutex); + free(this->mrls); this->mrls = NULL; } -static void dvd_plugin_dispose (input_plugin_t *this_gen ) { - dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; - int i; +#ifdef __sun +/* + * Check the environment, if we're running under sun's + * vold/rmmount control. + */ +static void +check_solaris_vold_device(dvdnav_input_plugin_t *this) +{ + char *volume_device; + char *volume_name; + char *volume_action; + char *device; + struct stat stb; - read_cache_free (this->read_cache); + if ((volume_device = getenv("VOLUME_DEVICE")) != NULL && + (volume_name = getenv("VOLUME_NAME")) != NULL && + (volume_action = getenv("VOLUME_ACTION")) != NULL && + strcmp(volume_action, "insert") == 0) { - for (i = 0; i < MAX_DIR_ENTRIES; i++) { - free (this->filelist[i]); - free (this->filelist2[i]); + device = malloc(strlen(volume_device) + strlen(volume_name) + 2); + if (device == NULL) + return; + sprintf(device, "%s/%s", volume_device, volume_name); + if (stat(device, &stb) != 0 || !S_ISCHR(stb.st_mode)) { + free(device); + return; + } + this->dvd_device = device; } - - free (this->mrls); - free (this); } - +#endif input_plugin_t *init_input_plugin (int iface, xine_t *xine) { + dvdnav_input_plugin_t *this; + config_values_t *config = xine->config; + + trace_print("Called\n"); + + switch (iface) { + case 8: + this = (dvdnav_input_plugin_t *) malloc (sizeof (dvdnav_input_plugin_t)); + + this->input_plugin.interface_version = INPUT_PLUGIN_IFACE_VERSION; + this->input_plugin.get_capabilities = dvdnav_plugin_get_capabilities; + this->input_plugin.open = dvdnav_plugin_open; + this->input_plugin.read = dvdnav_plugin_read; + this->input_plugin.read_block = dvdnav_plugin_read_block; + this->input_plugin.seek = dvdnav_plugin_seek; + this->input_plugin.get_current_pos = dvdnav_plugin_get_current_pos; + this->input_plugin.get_length = dvdnav_plugin_get_length; + this->input_plugin.get_blocksize = dvdnav_plugin_get_blocksize; + this->input_plugin.get_dir = dvdnav_plugin_get_dir; + this->input_plugin.eject_media = dvdnav_plugin_eject_media; + this->input_plugin.get_mrl = dvdnav_plugin_get_mrl; + this->input_plugin.stop = dvdnav_plugin_stop; + this->input_plugin.close = dvdnav_plugin_close; + this->input_plugin.get_description = dvdnav_plugin_get_description; + this->input_plugin.get_identifier = dvdnav_plugin_get_identifier; + this->input_plugin.get_autoplay_list = dvdnav_plugin_get_autoplay_list; + this->input_plugin.get_optional_data = dvdnav_plugin_get_optional_data; + this->input_plugin.is_branch_possible = NULL; + this->input_plugin.dispose = dvdnav_plugin_dispose; + + this->config = config; + this->xine = xine; + this->dvdnav = NULL; + this->opened = 0; + this->buttonN = 0; + this->dvd_name[0] = 0; + this->dvd_name_length = 0; + this->mrls = NULL; + this->num_mrls = 0; + + pthread_mutex_init(&this->buf_mutex, NULL); + this->mem_stack = 0; + + xine_register_event_listener(this->xine, dvdnav_event_listener, this); + this->dvd_device = config->register_string(config, + "input.dvd_device", + DVD_PATH, + "device used for dvd drive", + NULL, + device_change_cb, (void *)this); + this->current_dvd_device = this->dvd_device; + + config->register_num(config, "input.dvd_region", + 1, + "Region that DVD player claims " + "to be (1 -> 8)", + "This only needs to be changed " + "if your DVD jumps to a screen " + "complaining about region code ", + region_changed_cb, + this); + config->register_string(config, "input.dvdnav_language", + "en", + "The default language for dvd", + "The dvdnav plugin tries to use this " + "language as a default. This must be a" + "two character ISO country code.", + language_changed_cb, this); + config->register_bool(config, "input.dvdnav_use_readahead", + 1, + "Do we use read-ahead caching?", + "This " + "may lead to jerky playback on low-end " + "machines.", + read_ahead_cb, this); + +#ifdef __sun + check_solaris_vold_device(this); +#endif - dvd_input_plugin_t *this; - config_values_t *config; - int i; - - if (iface != 8) { - LOG_MSG(xine, - _("dvd 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"), + return (input_plugin_t *) this; + break; + default: + fprintf(stderr, + "DVD Navigator 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; } - - this = (dvd_input_plugin_t *) xine_xmalloc (sizeof (dvd_input_plugin_t)); - config = xine->config; - this->xine = xine; - - for (i = 0; i < MAX_DIR_ENTRIES; i++) { - this->filelist[i] = (char *) xine_xmalloc(sizeof(char *) * 256); - this->filelist2[i] = (char *) xine_xmalloc(sizeof(char *) * 256); - } - - this->input_plugin.interface_version = INPUT_PLUGIN_IFACE_VERSION; - this->input_plugin.get_capabilities = dvd_plugin_get_capabilities; - this->input_plugin.open = dvd_plugin_open; - this->input_plugin.read = dvd_plugin_read; - this->input_plugin.read_block = dvd_plugin_read_block; - this->input_plugin.seek = dvd_plugin_seek; - this->input_plugin.get_current_pos = dvd_plugin_get_current_pos; - this->input_plugin.get_length = dvd_plugin_get_length; - this->input_plugin.get_blocksize = dvd_plugin_get_blocksize; - this->input_plugin.eject_media = dvd_plugin_eject_media; - this->input_plugin.close = dvd_plugin_close; - this->input_plugin.stop = dvd_plugin_stop; - this->input_plugin.get_identifier = dvd_plugin_get_identifier; - this->input_plugin.get_description = dvd_plugin_get_description; - this->input_plugin.get_dir = dvd_plugin_get_dir; - this->input_plugin.get_mrl = dvd_plugin_get_mrl; - this->input_plugin.get_autoplay_list = dvd_plugin_get_autoplay_list; - this->input_plugin.get_optional_data = dvd_plugin_get_optional_data; - this->input_plugin.dispose = dvd_plugin_dispose; - this->input_plugin.is_branch_possible= NULL; - /* disable branch until we fix the problems branching from - menu vob to video vob - this->input_plugin.is_branch_possible= dvd_plugin_is_branch_possible; - */ - - this->device = config->register_string(config, "input.dvd_device", DVD, - _("path to your local dvd device file"), - NULL, device_change_cb, (void *)this); - this->raw_device = config->register_string(config, "input.dvd_raw_device", RDVD, - _("path to a raw device set up for dvd access"), - NULL, rawdevice_change_cb, (void*)this); -#ifdef __sun - check_solaris_vold_device(this); -#endif - - this->mrls_allocated_entries = 0; - this->mrls = xine_xmalloc(sizeof(mrl_t*)); - - this->mrl = NULL; - this->config = config; - this->dvd_fd = -1; - this->raw_fd = -1; - - this->read_cache = read_cache_new (); - - return (input_plugin_t *) this; } + +/* + * $Log: input_dvd.c,v $ + * Revision 1.53 2002/08/08 17:49:21 richwareham + * First stage of DVD plugin -> dvdnav conversion + * + */ |