/* 2004.02.01 first released source code for IOMP */ /* * Copyright (C) 2000-2003 the xine project, * Rich Wareham * * This file is part of xine, a free 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.11 2004/02/12 06:40:48 rwlin Exp $ * */ /* This file was origninally part of the xine-dvdnav project * at http://dvd.sf.net/. */ /* TODO: * * - Proper internationalisation of strings. * - Failure dialogue. */ extern int XINE_SEG_ERROR; extern char *XINE_SEG_ERROR_FILE; //#define LOG_SEG_ERROR sprintf(XINE_SEG_ERROR_FILE,"%s (%d)",__FILE__,__LINE__); #define LOG_SEG_ERROR #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Standard includes */ #include #include #include #ifndef _MSC_VER #include #endif /* _MSC_VER */ #include #include #include #include #include #include #include #include #include #ifndef _MSC_VER #include #include #include #include #endif /* _MSC_VER */ #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) #include #include /* CDIOCALLOW etc... */ #elif defined(HAVE_LINUX_CDROM_H) #include #elif defined(HAVE_SYS_CDIO_H) #include #else #ifdef WIN32 #include /* read() */ #else #warning "This might not compile due to missing cdrom ioctls" #endif /* WIN32 */ #endif /* DVDNAV includes */ #ifdef HAVE_DVDNAV # include # include #else # define DVDNAV_COMPILE # include "dvdnav.h" # include "nav_read.h" #endif /* Xine includes */ #include "xineutils.h" #include "buffer.h" #include "xine_internal.h" #include "media_helper.h" /* Print debug messages? */ /* #define INPUT_DEBUG */ /* 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 DVD_PATH "/vol/dev/aliases/cdrom0" #define RDVD_PATH "" #elif WIN32 /* There really isn't a default on Windows! */ #define DVD_PATH "d:\\" #define RDVD_PATH "d:\\" #else #define DVD_PATH "/dev/dvd" #define RDVD_PATH "/dev/rdvd" #endif /* Some misc. defines */ #ifdef DVD_VIDEO_LB_LEN # define DVD_BLOCK_SIZE DVD_VIDEO_LB_LEN #else # define DVD_BLOCK_SIZE 2048 #endif /* Debugging macros */ #ifdef __GNUC__ # ifdef INPUT_DEBUG_TRACE # define trace_print(s, args...) printf("input_dvd: " __func__ ": " s, ##args); # else # define trace_print(s, args...) /* Nothing */ # endif #else # ifndef _MSC_VER # define trace_print(s, ...) /* Nothing */ # else # ifdef INPUT_DEBUG_TRACE # define trace_print printf # else # define trace_print() /* Nothing */ # endif /* INPUT_DEBUG_TRACE */ # endif /* _MSC_VER */ #endif /* Array to hold MRLs returned by get_autoplay_list */ #define MAX_DIR_ENTRIES 1250 #define MAX_STR_LEN 255 #if defined (__FreeBSD__) # define off64_t off_t # define lseek64 lseek #endif static const char *dvdnav_menu_table[] = { NULL, NULL, "Title", "Root", "Subpicture", "Audio", "Angle", "Part" }; typedef struct { input_plugin_t input_plugin; /* Parent input plugin type */ xine_stream_t *stream; xine_event_queue_t *event_queue; int pause_timer; /* Cell still-time timer */ int pause_counter; time_t pause_end_time; int64_t pg_length; int64_t pgc_length; int64_t cell_start; int64_t pg_start; int32_t buttonN; int typed_buttonN;/* for XINE_EVENT_INPUT_NUMBER_* */ int32_t mouse_buttonN; int mouse_in; /* Flags */ int opened; /* 1 if the DVD device is already open */ int seekable; /* are we seekable? */ /* xine specific variables */ char *current_dvd_device; /* DVD device currently open */ char *mrl; /* Current MRL */ int mode; dvdnav_t *dvdnav; /* Handle for libdvdnav */ const char *dvd_name; /* xine_mrl_t **mrls; int num_mrls; */ char filelist[MAX_DIR_ENTRIES][MAX_STR_LEN]; char ui_title[MAX_STR_LEN + 1]; /* 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]; int freeing; } dvd_input_plugin_t; typedef struct { input_class_t input_class; xine_t *xine; config_values_t *config; /* Pointer to XineRC config file */ int mrls_allocated_entries; xine_mrl_t **mrls; char *dvd_device; /* Default DVD device */ dvd_input_plugin_t *ip; int32_t read_ahead_flag; int32_t seek_mode; int32_t language; int32_t region; char *filelist2[MAX_DIR_ENTRIES]; } dvd_input_class_t; static void dvd_handle_events(dvd_input_plugin_t *this); static void xine_dvd_send_button_update(dvd_input_plugin_t *this, int mode); /* Callback on device name change */ static void device_change_cb(void *data, xine_cfg_entry_t *cfg) { dvd_input_class_t *class = (dvd_input_class_t *) data; class->dvd_device = cfg->str_value; } static uint32_t dvd_plugin_get_capabilities (input_plugin_t *this_gen) { dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen; trace_print("Called\n"); return INPUT_CAP_BLOCK | /* TODO: figure out if there is any "allow copying" flag on DVD. * maybe set INPUT_CAP_RIP_FORBIDDEN only for encrypted media? */ INPUT_CAP_RIP_FORBIDDEN | #if CAN_SEEK (this->seekable ? INPUT_CAP_SEEKABLE : 0) | #endif INPUT_CAP_AUDIOLANG | INPUT_CAP_SPULANG | INPUT_CAP_CHAPTERS; } void read_ahead_cb(void *this_gen, xine_cfg_entry_t *entry) { dvd_input_class_t *class = (dvd_input_class_t*)this_gen; if(!class) return; class->read_ahead_flag = entry->num_value; if(class->ip) { dvd_input_plugin_t *this = class->ip; dvdnav_set_readahead_flag(this->dvdnav, entry->num_value); } } void seek_mode_cb(void *this_gen, xine_cfg_entry_t *entry) { dvd_input_class_t *class = (dvd_input_class_t*)this_gen; if(!class) return; class->seek_mode = entry->num_value; if(class->ip) { dvd_input_plugin_t *this = class->ip; dvdnav_set_PGC_positioning_flag(this->dvdnav, !entry->num_value); } } void region_changed_cb (void *this_gen, xine_cfg_entry_t *entry) { dvd_input_class_t *class = (dvd_input_class_t*)this_gen; if(!class) return; class->region = entry->num_value; if(class->ip && ((entry->num_value >= 1) && (entry->num_value <= 8))) { dvd_input_plugin_t *this = class->ip; dvdnav_set_region_mask(this->dvdnav, 1<<(entry->num_value-1)); } } void language_changed_cb(void *this_gen, xine_cfg_entry_t *entry) { dvd_input_class_t *class = (dvd_input_class_t*)this_gen; if(!class) return; class->language = entry->str_value[0] << 8 | entry->str_value[1]; if(class->ip) { dvd_input_plugin_t *this = class->ip; 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); } } static void send_mouse_enter_leave_event(dvd_input_plugin_t *this, int direction) { if(direction && this->mouse_in) this->mouse_in = !this->mouse_in; if(direction != this->mouse_in) { xine_event_t event; xine_spu_button_t spu_event; spu_event.direction = direction; spu_event.button = this->mouse_buttonN; event.type = XINE_EVENT_SPU_BUTTON; event.stream = this->stream; event.data = &spu_event; event.data_length = sizeof(spu_event); xine_event_send(this->stream, &event); this->mouse_in = direction; } if(!direction) this->mouse_buttonN = -1; } static void update_title_display(dvd_input_plugin_t *this) { xine_event_t uevent; xine_ui_data_t data; int tt=-1, pr=-1; size_t ui_str_length=0; if(!this || !(this->stream)) return; /* Set title/chapter display */ 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(this->ui_title, MAX_STR_LEN, "Title %i, Chapter %i, Angle %i of %i", tt,pr,cur_angle, num_angle); } else { snprintf(this->ui_title, MAX_STR_LEN, "Title %i, Chapter %i", tt,pr); } } else if (tt == 0 && dvdnav_menu_table[pr]) { snprintf(this->ui_title, MAX_STR_LEN, "DVD %s Menu", dvdnav_menu_table[pr]); } else { strcpy(this->ui_title, "DVD Menu"); } ui_str_length = strlen(this->ui_title); if (this->dvd_name && this->dvd_name[0] && (ui_str_length + strlen(this->dvd_name) < MAX_STR_LEN)) { snprintf(this->ui_title+ui_str_length, MAX_STR_LEN - ui_str_length, ", %s", this->dvd_name); } #ifdef INPUT_DEBUG printf("input_dvd: Changing title to read '%s'\n", this->ui_title); #endif uevent.type = XINE_EVENT_UI_SET_TITLE; uevent.stream = this->stream; uevent.data = &data; uevent.data_length = sizeof(data);; memcpy(data.str, this->ui_title, strlen(this->ui_title) + 1); data.str_len = strlen(this->ui_title) + 1; xine_event_send(this->stream, &uevent); } static void dvd_plugin_stop (input_plugin_t *this_gen) { dvd_input_plugin_t *this = (dvd_input_plugin_t*) this_gen; if (this->dvdnav) { dvdnav_still_skip(this->dvdnav); } } static void dvd_plugin_dispose (input_plugin_t *this_gen) { dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen; trace_print("Called\n"); if (this->event_queue) xine_event_dispose_queue (this->event_queue); if (this->dvdnav) { dvdnav_close(this->dvdnav); /* raise the freeing flag, so that the plugin will be freed as soon * as all buffers have returned to the libdvdnav read ahead cache */ this->freeing = 1; } else { pthread_mutex_destroy(&this->buf_mutex); free(this); } } /* Align pointer |p| to alignment |align| */ #define PTR_ALIGN(p, align) ((void*) (((long)(p) + (align) - 1) & ~((align)-1)) ) static void dvd_build_mrl_list(dvd_input_plugin_t *this) { /* FIXME */ #if 0 int num_titles, *num_parts; /* skip DVD if already open */ if (this->opened) return; if (this->class->mrls) { free(this->class->mrls); this->class->mrls = NULL; this->class->num_mrls = 0; } if (dvdnav_open(&(this->dvdnav), this->dvd_device) == DVDNAV_STATUS_ERR) { return; } this->current_dvd_device = this->dvd_device; this->opened = 1; dvdnav_get_number_of_titles(this->dvdnav, &num_titles); if ((num_parts = (int *) calloc(num_titles, sizeof(int)))) { struct xine_mrl_align_s { char dummy; xine_mrl_t mrl; }; int xine_mrl_alignment = offsetof(struct xine_mrl_align_s, mrl); int num_mrls = 1, i; /* for each title, count the number of parts */ for (i = 1; i <= num_titles; i++) { num_parts[i-1] = 0; /* dvdnav_title_play(this->dvdnav, i); */ dvdnav_get_number_of_parts(this->dvdnav, i, &num_parts[i-1]); num_mrls += num_parts[i-1]; /* num_mrls = total number of programs */ } /* allocate enough memory for: * - a list of pointers to mrls sizeof(xine_mrl_t *) * (num_mrls+1) * - possible alignment of the mrl array * - an array of mrl structures sizeof(xine_mrl_t) * num_mrls * - enough chars for every filename sizeof(char)*25 * num_mrls * - "dvd:/000000.000000\0" = 25 chars */ if ((this->mrls = (xine_mrl_t **) malloc(sizeof(xine_mrl_t *) + num_mrls * (sizeof(xine_mrl_t*) + sizeof(xine_mrl_t) + 25*sizeof(char)) + xine_mrl_alignment))) { /* the first mrl struct comes after the pointer list */ xine_mrl_t *mrl = PTR_ALIGN(&this->mrls[num_mrls+1], xine_mrl_alignment); /* 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->class->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->class->mrls[pos] = NULL; /* terminate list */ } free(num_parts); } #else return; #endif } static void dvd_plugin_free_buffer(buf_element_t *buf) { dvd_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); if (this->freeing && !this->mem_stack) { /* all buffers returned, we can free the plugin now */ pthread_mutex_destroy(&this->buf_mutex); free(this); } } static int64_t dvdnav_timer_end_time=0; static void* dvdnav_timer_count_down_thread(void *arg) { dvd_input_plugin_t *this=(dvd_input_plugin_t *)arg; // while(this->stream->metronom->clock->get_current_time(this->stream->metronom->clock)dvdnav); if(xine_get_param(this->stream, XINE_PARAM_SPEED)==XINE_SPEED_PAUSE) { xine_set_speed(this->stream,XINE_SPEED_NORMAL); this->stream->vobu_still=0; } printf("!!!!!!!!!!timer ring!!!!!!!!!!!!!\n"); fflush(stdout); } 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; dvdnav_status_t result; int event, len; int finished = 0; unsigned char *block; LOG_SEG_ERROR; if(fifo == NULL) { printf("input_dvd: values of \\beta will give rise to dom!\n"); return NULL; } LOG_SEG_ERROR; /* Read buffer */ if (fifo) { buf = fifo->buffer_pool_alloc (fifo); } else { LOG_SEG_ERROR; } block = buf->mem; LOG_SEG_ERROR; while(!finished) { dvd_handle_events(this); 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) { printf("input_dvd: Error getting next block from DVD (%s)\n", dvdnav_err_to_string(this->dvdnav)); xine_message(this->stream, XINE_MSG_READ_ERROR, dvdnav_err_to_string(this->dvdnav), NULL); if (block != buf->mem) dvdnav_free_cache_block(this->dvdnav, block); buf->free_buffer(buf); return NULL; } switch(event) { case DVDNAV_BLOCK_OK: { buf->content = block; buf->type = BUF_DEMUX_BLOCK; /* Make sure we don't think we are still paused */ this->pause_timer = 0; /* we got a block, so we might be seekable here */ this->seekable = 1; finished = 1; } break; case DVDNAV_NOP: break; case DVDNAV_STILL_FRAME: { dvdnav_still_event_t *still_event = (dvdnav_still_event_t*)block; buf->type = BUF_CONTROL_NOP; finished = 1; /* stills are not seekable */ this->seekable = 0; /* Xine's method of doing still-frames */ if (this->pause_timer == 0) { /* added by rwlin@avamax.com */ /*if(still_event->length & 0x80000000) buf->type=0x010b0000; */ buf->type=BUF_CONTROL_STILL_FRAME; buf->decoder_info[0]=still_event->type; buf->decoder_info[1]=still_event->length & ~0x80000000; if(buf->decoder_info[1]==0xff) buf->decoder_info[1]=(unsigned int)-1; if(1 /*buf->decoder_info[1]==(unsigned int)-1*/) { this->pause_timer = (still_event->length & ~0x80000000); this->pause_end_time = time(NULL) + this->pause_timer; this->pause_counter = 0; } else { dvdnav_still_skip(this->dvdnav); } break; } if(this->pause_timer == 0xff) { this->pause_counter++; xine_usec_sleep(50000); 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++; #ifdef INPUT_DEBUG printf("input_dvd: Stillframe! (pause_timer = 0x%02x) counter=%d\n", still_event->length, this->pause_counter); #endif xine_usec_sleep(50000); 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; buf->decoder_info[3] = dvdnav_spu_channel_validity(this->dvdnav); /* added by rwlin@avamax.com */ #ifdef INPUT_DEBUG printf("input_dvd: SPU stream wide %d, letterbox %d, pan&scan %d\n", stream_event->physical_wide, stream_event->physical_letterbox, stream_event->physical_pan_scan); #endif 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; buf->decoder_info[1] = dvdnav_audio_channel_validity(this->dvdnav); /* add by george */ #ifdef INPUT_DEBUG printf("input_dvd: AUDIO stream %d\n", stream_event->physical); #endif finished = 1; } break; case DVDNAV_HIGHLIGHT: xine_dvd_send_button_update(this, 0); break; case DVDNAV_VTS_CHANGE: { int aspect, permission; #ifdef INPUT_DEBUG printf("input_dvd: VTS change\n"); #endif /* 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: { dvdnav_cell_change_event_t *cell_event = (dvdnav_cell_change_event_t*) (block); xine_event_t event; /* Tell xine to update the UI */ event.type = XINE_EVENT_UI_CHANNELS_CHANGED; event.stream = this->stream; event.data = NULL; event.data_length = 0; xine_event_send(this->stream, &event); update_title_display(this); this->pg_length = cell_event->pg_length; this->pgc_length = cell_event->pgc_length; this->cell_start = cell_event->cell_start; this->pg_start = cell_event->pg_start; } break; case DVDNAV_HOP_CHANNEL: xine_demux_flush_engine(this->stream); 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_DVD; buf->decoder_flags |= BUF_FLAG_SPECIAL; buf->decoder_info[1] = BUF_SPECIAL_SPU_DVD_SUBTYPE; buf->decoder_info[2] = SPU_DVD_SUBTYPE_CLUT; finished = 1; } break; case DVDNAV_STOP: Stop: { /* added by rwlin@avamax.com */ int buffers; for(buffers=2;buffers>1;xine_usec_sleep(50000)) { buffers = this->stream->video_fifo->size(this->stream->video_fifo); if (this->stream->audio_fifo) buffers += this->stream->audio_fifo->size(this->stream->audio_fifo); } if (buf->mem != block) dvdnav_free_cache_block(this->dvdnav, block); buf->free_buffer(buf); /* return NULL to indicate end of stream */ return NULL; } case DVDNAV_WAIT: { int buffers = this->stream->video_fifo->size(this->stream->video_fifo); if (this->stream->audio_fifo) buffers += this->stream->audio_fifo->size(this->stream->audio_fifo); /* we wait until the fifos are empty, ... well, we allow one remaining buffer, * because a flush might be in progress. */ if (buffers <= 1) dvdnav_wait_skip(this->dvdnav); else xine_usec_sleep(50000); // ---------------- george add ----------------- if(!this->stream->demux_thread_running) { finished = 1; goto Stop; } //---------------------------------------------- } break; /* by rwlin@avamax.com */ case DVDNAV_REGISTER_TIMER: { pthread_t *th; int *sec=(int *)block; if(sec && *sec>0) { //dvdnav_timer_end_time=this->stream->metronom->clock->get_current_time (this->stream->metronom->clock) + XINE_SPEED_NORMAL*(*sec)*(90000/4); dvdnav_timer_end_time=time(NULL)+*sec; pthread_create(&th,NULL,dvdnav_timer_count_down_thread,this); printf("input_dvd.c: received alarm in %d seconds, end clock=%ld\n",*sec,dvdnav_timer_end_time); } } break; default: printf("input_dvd: FIXME: Unknown event (%i)\n", event); break; } } 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 = dvd_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. */ printf("input_dvd: too many buffers issued, memory stack exceeded\n"); memcpy(buf->mem, block, DVD_BLOCK_SIZE); dvdnav_free_cache_block(this->dvdnav, block); buf->content = buf->mem; } pthread_mutex_unlock(&this->buf_mutex); } if (this->pg_length && this->pgc_length) { int pos, length; dvdnav_get_position(this->dvdnav, &pos, &length); buf->extra_info->input_pos = pos * (off_t)DVD_BLOCK_SIZE; buf->extra_info->input_length = length * (off_t)DVD_BLOCK_SIZE; switch (((dvd_input_class_t *)this->input_plugin.input_class)->seek_mode) { case 0: /* PGC based seeking */ buf->extra_info->total_time = this->pgc_length / 90; buf->extra_info->input_time = this->cell_start / 90; break; case 1: /* PG based seeking */ buf->extra_info->total_time = this->pg_length / 90; buf->extra_info->input_time = (this->cell_start - this->pg_start) / 90; break; } } return buf; } static off_t dvd_plugin_read (input_plugin_t *this_gen, char *ch_buf, off_t len) { /* dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen; */ /* FIXME: Tricking the demux_mpeg_block plugin */ ch_buf[0] = 0; ch_buf[1] = 0; ch_buf[2] = 0x01; ch_buf[3] = 0xba; return 1; } static off_t dvd_plugin_get_current_pos (input_plugin_t *this_gen){ dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen; uint32_t pos=0; uint32_t length=1; dvdnav_status_t result; LOG_SEG_ERROR if(!this || !this->dvdnav) { return 0; } result = dvdnav_get_position(this->dvdnav, &pos, &length); return (off_t)pos * (off_t)DVD_BLOCK_SIZE; } 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; trace_print("Called\n"); LOG_SEG_ERROR if(!this || !this->dvdnav) { return -1; } dvdnav_sector_search(this->dvdnav, offset / DVD_BLOCK_SIZE , origin); return dvd_plugin_get_current_pos(this_gen); } static off_t dvd_plugin_get_length (input_plugin_t *this_gen) { dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen; uint32_t pos=0; uint32_t length=1; dvdnav_status_t result; if(!this || !this->dvdnav) { return 0; } result = dvdnav_get_position(this->dvdnav, &pos, &length); return (off_t)length * (off_t)DVD_BLOCK_SIZE; } static uint32_t dvd_plugin_get_blocksize (input_plugin_t *this_gen) { return DVD_BLOCK_SIZE; } static char* dvd_plugin_get_mrl (input_plugin_t *this_gen) { dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen; return this->mrl; } static void xine_dvd_send_button_update(dvd_input_plugin_t *this, int mode) { int32_t button; int32_t show; if (!this || !this->stream || this->stream->stream_info[XINE_STREAM_INFO_IGNORE_SPU]) return; if (!this->stream->spu_decoder_plugin || this->stream->spu_decoder_streamtype != ((BUF_SPU_DVD >> 16) & 0xFF)) { /* the proper SPU decoder has not been initialized yet, * so we send a dummy buffer to trigger this */ buf_element_t *buf = this->stream->video_fifo->buffer_pool_alloc(this->stream->video_fifo); buf->size = 0; buf->type = BUF_SPU_DVD; this->stream->video_fifo->insert(this->stream->video_fifo, buf); while (!this->stream->spu_decoder_plugin || this->stream->spu_decoder_streamtype != ((BUF_SPU_DVD >> 16) & 0xFF)) xine_usec_sleep(50000); } dvdnav_get_current_highlight(this->dvdnav, &button); if (button == this->buttonN && (mode == 0) ) return; this->buttonN = button; /* Avoid duplicate sending of button info */ #ifdef INPUT_DEBUG printf("input_dvd: sending_button_update button=%d mode=%d\n", button, mode); #endif /* Do we want to show or hide the button? */ /* libspudec will control hiding */ show = mode + 1; /* mode=0 select, 1 activate. */ this->stream->spu_decoder_plugin->set_button (this->stream->spu_decoder_plugin, button, mode + 1); } extern time_t still_end_time; static void dvd_handle_events(dvd_input_plugin_t *this) { dvd_input_class_t *class = (dvd_input_class_t*)this->input_plugin.input_class; config_values_t *config = class->config; /* Pointer to XineRC config file */ xine_event_t *event; while ((event = xine_event_get(this->event_queue))) { if(!this->dvdnav) { xine_event_free(event); return; } switch(event->type) { case XINE_EVENT_INPUT_MENU1: dvdnav_menu_call(this->dvdnav, DVD_MENU_Escape); 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_Root); break; case XINE_EVENT_INPUT_MENU4: dvdnav_menu_call(this->dvdnav, DVD_MENU_Subpicture); break; case XINE_EVENT_INPUT_MENU5: dvdnav_menu_call(this->dvdnav, DVD_MENU_Audio); break; case XINE_EVENT_INPUT_MENU6: dvdnav_menu_call(this->dvdnav, DVD_MENU_Angle); break; case XINE_EVENT_INPUT_MENU7: dvdnav_menu_call(this->dvdnav, DVD_MENU_Part); break; case XINE_EVENT_INPUT_NEXT: { cfg_entry_t* entry = config->lookup_entry(config, "input.dvd_skip_behaviour"); int title = 0, part = 0; /* added by rwlin@avamax.com */ dvdnav_current_title_info(this->dvdnav, &title, &part); if(event->data) { xine_input_next_t *p=(xine_input_next_t *)event->data; if(p->title || p->chapter) { if(p->title) { if(title!=p->title && xine_get_param(this->stream, XINE_PARAM_SPEED)==XINE_SPEED_PAUSE) { xine_set_speed(this->stream, XINE_SPEED_NORMAL); still_end_time=(time_t)0; } title=p->title; } part=p->chapter; dvdnav_part_play(this->dvdnav, title, part); } else if(p->step && title>0) dvdnav_part_play(this->dvdnav, title, (part+=p->step)); } else { switch (entry->num_value) { case 0: /* skip by program */ dvdnav_next_pg_search(this->dvdnav); break; case 1: /* skip by part */ if (dvdnav_current_title_info(this->dvdnav, &title, &part) && title > 0) dvdnav_part_play(this->dvdnav, title, ++part); break; case 2: /* skip by title */ if (dvdnav_current_title_info(this->dvdnav, &title, &part) && title > 0) dvdnav_part_play(this->dvdnav, ++title, 1); break; } } } break; case XINE_EVENT_INPUT_PREVIOUS: { cfg_entry_t *entry = config->lookup_entry(config, "input.dvd_skip_behaviour"); int title = 0, part = 0; /* added by rwlin@avamax.com */ dvdnav_current_title_info(this->dvdnav, &title, &part); if(event->data) { xine_input_next_t *p=(xine_input_next_t *)event->data; if(p->title || p->chapter) { if(p->title) { if(title!=p->title && xine_get_param(this->stream, XINE_PARAM_SPEED)==XINE_SPEED_PAUSE) { xine_set_speed(this->stream, XINE_SPEED_NORMAL); still_end_time=(time_t)0; } title=p->title; } part=p->chapter; dvdnav_part_play(this->dvdnav, title, part); } else if(p->step && title>0) dvdnav_part_play(this->dvdnav, title, (part-=p->step)); } else { switch (entry->num_value) { case 0: /* skip by program */ dvdnav_prev_pg_search(this->dvdnav); break; case 1: /* skip by part */ if (dvdnav_current_title_info(this->dvdnav, &title, &part) && title > 0) dvdnav_part_play(this->dvdnav, title, --part); break; case 2: /* skip by title */ if (dvdnav_current_title_info(this->dvdnav, &title, &part) && title > 0) dvdnav_part_play(this->dvdnav, --title, 1); break; } } } 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); #ifdef INPUT_DEBUG printf("input_dvd: Changing to angle %i\n", current); #endif 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); #ifdef INPUT_DEBUG printf("input_dvd: Changing to angle %i\n", current); #endif update_title_display(this); } break; case XINE_EVENT_INPUT_SELECT: { pci_t nav_pci; if(!this->stream || !this->stream->spu_decoder_plugin) { return; } if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) { if (dvdnav_button_activate(this->dvdnav, &nav_pci) == DVDNAV_STATUS_OK) xine_dvd_send_button_update(this, 1); } } break; case XINE_EVENT_INPUT_MOUSE_BUTTON: { pci_t nav_pci; if(!this->stream || !this->stream->spu_decoder_plugin) { return; } if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) { xine_input_data_t *input = event->data; if (dvdnav_mouse_activate(this->dvdnav, &nav_pci, input->x, input->y) == DVDNAV_STATUS_OK) { xine_dvd_send_button_update(this, 1); if(this->mouse_in) send_mouse_enter_leave_event(this, 0); this->mouse_buttonN = -1; } } } break; case XINE_EVENT_INPUT_BUTTON_FORCE: /* For libspudec to feedback forced button select from NAV PCI packets. */ { pci_t nav_pci; int *but = event->data; #ifdef INPUT_DEBUG printf("input_dvd: BUTTON_FORCE %d\n", *but); #endif if(!this->stream || !this->stream->spu_decoder_plugin) return; if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) dvdnav_button_select(this->dvdnav, &nav_pci, *but); } break; case XINE_EVENT_INPUT_MOUSE_MOVE: { pci_t nav_pci; if(!this->stream || !this->stream->spu_decoder_plugin) return; if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) { xine_input_data_t *input = event->data; /* printf("input_dvd: Mouse move (x,y) = (%i,%i)\n", input->x, input->y); */ if(dvdnav_mouse_select(this->dvdnav, &nav_pci, input->x, input->y) == DVDNAV_STATUS_OK) { int32_t button; dvdnav_get_current_highlight(this->dvdnav, &button); if(this->mouse_buttonN != button) { this->mouse_buttonN = button; send_mouse_enter_leave_event(this, 1); } } else { if(this->mouse_in) send_mouse_enter_leave_event(this, 0); } } } break; case XINE_EVENT_INPUT_UP: { pci_t nav_pci; if(!this->stream || !this->stream->spu_decoder_plugin) return; if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) { dvdnav_upper_button_select(this->dvdnav, &nav_pci); if(this->mouse_in) send_mouse_enter_leave_event(this, 0); } break; } case XINE_EVENT_INPUT_DOWN: { pci_t nav_pci; if(!this->stream || !this->stream->spu_decoder_plugin) return; if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) { dvdnav_lower_button_select(this->dvdnav, &nav_pci); if(this->mouse_in) send_mouse_enter_leave_event(this, 0); } break; } case XINE_EVENT_INPUT_LEFT: { pci_t nav_pci; if(!this->stream || !this->stream->spu_decoder_plugin) return; if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) { dvdnav_left_button_select(this->dvdnav, &nav_pci); if(this->mouse_in) send_mouse_enter_leave_event(this, 0); } break; } case XINE_EVENT_INPUT_RIGHT: { pci_t nav_pci; if(!this->stream || !this->stream->spu_decoder_plugin) return; if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) { dvdnav_right_button_select(this->dvdnav, &nav_pci); if(this->mouse_in) send_mouse_enter_leave_event(this, 0); } break; } case XINE_EVENT_INPUT_NUMBER_9: this->typed_buttonN++; case XINE_EVENT_INPUT_NUMBER_8: this->typed_buttonN++; case XINE_EVENT_INPUT_NUMBER_7: this->typed_buttonN++; case XINE_EVENT_INPUT_NUMBER_6: this->typed_buttonN++; case XINE_EVENT_INPUT_NUMBER_5: this->typed_buttonN++; case XINE_EVENT_INPUT_NUMBER_4: this->typed_buttonN++; case XINE_EVENT_INPUT_NUMBER_3: this->typed_buttonN++; case XINE_EVENT_INPUT_NUMBER_2: this->typed_buttonN++; case XINE_EVENT_INPUT_NUMBER_1: this->typed_buttonN++; case XINE_EVENT_INPUT_NUMBER_0: { pci_t nav_pci; if(!this->stream || !this->stream->spu_decoder_plugin) return; if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) { if (dvdnav_button_select_and_activate(this->dvdnav, &nav_pci, this->typed_buttonN) == DVDNAV_STATUS_OK) { xine_dvd_send_button_update(this, 1); if(this->mouse_in) send_mouse_enter_leave_event(this, 0); } this->typed_buttonN = 0; } break; } case XINE_EVENT_INPUT_NUMBER_10_ADD: this->typed_buttonN += 10; } xine_event_free(event); } return; } static int dvd_plugin_get_optional_data (input_plugin_t *this_gen, void *data, int data_type) { dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen; int title,part; switch(data_type & 0x0000FFFF) { /* modified by rwlin@avamax.com */ case INPUT_OPTIONAL_DATA_AUDIOLANG: { uint16_t lang; int channel = *((int *)data); int8_t dvd_channel; /* Be paranoid */ if(this && this->stream && this->dvdnav) { if(!(dvdnav_is_domain_vts(this->dvdnav))) { sprintf(data, "%s", "menu"); if (channel <= 0) return INPUT_OPTIONAL_SUCCESS; else return INPUT_OPTIONAL_UNSUPPORTED; } if (channel == -1) dvd_channel = dvdnav_get_audio_logical_stream(this->dvdnav, this->stream->audio_channel_auto); else dvd_channel = dvdnav_get_audio_logical_stream(this->dvdnav, channel); if(dvd_channel != -1) { lang = dvdnav_audio_stream_to_lang(this->dvdnav, dvd_channel); if(lang != 0xffff) sprintf(data, " %c%c", lang >> 8, lang & 0xff); else sprintf(data, " %c%c", '?', '?'); return INPUT_OPTIONAL_SUCCESS; } else { if (channel == -1) { sprintf(data, "%s", "none"); return INPUT_OPTIONAL_SUCCESS; } } } return INPUT_OPTIONAL_UNSUPPORTED; } break; case INPUT_OPTIONAL_DATA_SPULANG: { uint16_t lang; int channel = *((int *)data); int8_t dvd_channel; /* Be paranoid */ if(this && this->stream && this->dvdnav) { if(!(dvdnav_is_domain_vts(this->dvdnav))) { sprintf(data, "%s", "menu"); if (channel <= 0) return INPUT_OPTIONAL_SUCCESS; else return INPUT_OPTIONAL_UNSUPPORTED; } if(channel == -1) dvd_channel = dvdnav_get_spu_logical_stream(this->dvdnav, this->stream->spu_channel_auto); else dvd_channel = dvdnav_get_spu_logical_stream(this->dvdnav, channel); if(dvd_channel != -1) { lang = dvdnav_spu_stream_to_lang(this->dvdnav, dvd_channel); if(lang != 0xffff) sprintf(data, " %c%c", lang >> 8, lang & 0xff); else sprintf(data, " %c%c", '?', '?'); return INPUT_OPTIONAL_SUCCESS; } else { if(channel == -1) { sprintf(data, "%s", "none"); return INPUT_OPTIONAL_SUCCESS; } } } return INPUT_OPTIONAL_UNSUPPORTED; } break; /* added by rwlin@avamax.com */ case INPUT_OPTIONAL_DATA_TITLES_TOTAL : return (data && dvdnav_get_number_of_titles(this->dvdnav,data)==DVDNAV_STATUS_OK) ? INPUT_OPTIONAL_SUCCESS : INPUT_OPTIONAL_UNSUPPORTED; case INPUT_OPTIONAL_DATA_CHAPTERS_TOTAL: dvdnav_current_title_info(this->dvdnav, &title, &part); return (dvdnav_get_number_of_parts(this->dvdnav,title,data)==DVDNAV_STATUS_OK) ? INPUT_OPTIONAL_SUCCESS : INPUT_OPTIONAL_UNSUPPORTED; // return (data && dvdnav_get_number_of_parts(this->dvdnav,(data_type >> 16 ) & 0x0000FFFF,data)==DVDNAV_STATUS_OK) ? INPUT_OPTIONAL_SUCCESS : INPUT_OPTIONAL_UNSUPPORTED; /* add by george */ case INPUT_OPTIONAL_DATA_TITLES_CURRENT : if(dvdnav_current_title_info(this->dvdnav, &title, &part)==DVDNAV_STATUS_OK) { *((int *)data) = title; return INPUT_OPTIONAL_SUCCESS; } else return INPUT_OPTIONAL_UNSUPPORTED; break; /* add by george */ case INPUT_OPTIONAL_DATA_CHAPTERS_CURRENT : if(dvdnav_current_title_info(this->dvdnav, &title, &part)==DVDNAV_STATUS_OK) { *((int *)data) = part; return INPUT_OPTIONAL_SUCCESS; } else return INPUT_OPTIONAL_UNSUPPORTED; break; case INPUT_OPTIONAL_DATA_DVDNAV_DOMAIN: { int *dm=(int *)data; if(dm==NULL) return INPUT_OPTIONAL_UNSUPPORTED; *dm=0; if(dvdnav_is_domain_fp(this->dvdnav)) (*dm) |= FP_DOMAIN; if(dvdnav_is_domain_vts(this->dvdnav)) (*dm) |= VTS_DOMAIN; if(dvdnav_is_domain_vmgm(this->dvdnav)) (*dm) |= VMGM_DOMAIN; if(dvdnav_is_domain_vtsm(this->dvdnav)) (*dm) |= VTSM_DOMAIN ; return INPUT_OPTIONAL_SUCCESS; } } return INPUT_OPTIONAL_UNSUPPORTED; } #ifdef __sun /* * Check the environment, if we're running under sun's * vold/rmmount control. */ static void check_solaris_vold_device(dvd_input_class_t *this) { char *volume_device; char *volume_name; char *volume_action; char *device; struct stat stb; if ((volume_device = getenv("VOLUME_DEVICE")) != NULL && (volume_name = getenv("VOLUME_NAME")) != NULL && (volume_action = getenv("VOLUME_ACTION")) != NULL && strcmp(volume_action, "insert") == 0) { 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; } } #endif static int dvd_plugin_open (input_plugin_t *this_gen) { dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen; dvd_input_class_t *class = (dvd_input_class_t*)this_gen->input_class; char *locator; int last_slash = 0; dvdnav_status_t ret; char *intended_dvd_device; xine_event_t event; static char *handled_mrl = "dvd:/"; xine_cfg_entry_t region_entry, lang_entry, cache_entry; trace_print("Called\n"); /* we already checked the "dvd:/" MRL above */ locator = &this->mrl[strlen(handled_mrl)]; while (*locator == '/') locator++; #ifndef _MSC_VER /* we skipped at least one slash, get it back */ locator--; #endif /* Attempt to parse MRL */ last_slash = strlen(locator); while(last_slash && ((locator[last_slash] != '/') && (locator[last_slash] != '\\'))) last_slash--; if(last_slash) { /* we have an alternative dvd_path */ intended_dvd_device = locator; intended_dvd_device[last_slash] = '\0'; locator += last_slash; /* do not use the raw device for the alternative */ xine_setenv("DVDCSS_RAW_DEVICE", "", 1); #ifdef _MSC_VER locator++; #endif }else{ xine_cfg_entry_t raw_device; if (xine_config_lookup_entry(this->stream->xine, "input.dvd_raw_device", &raw_device)) xine_setenv("DVDCSS_RAW_DEVICE", raw_device.str_value, 1); intended_dvd_device=class->dvd_device; } #ifndef _MSC_VER locator++; #endif if(locator[0]) { this->mode = MODE_TITLE; } else { this->mode = MODE_NAVIGATE; } if(this->opened) { if ( intended_dvd_device==this->current_dvd_device ) { /* Already open, so skip opening */ dvdnav_reset(this->dvdnav); } 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) { if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG) printf("input_dvd: Error opening DVD device\n"); xine_message (this->stream, XINE_MSG_READ_ERROR, intended_dvd_device, NULL); 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) { if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG) printf("input_dvd: Error opening DVD device\n"); xine_message (this->stream, XINE_MSG_READ_ERROR, intended_dvd_device, NULL); return 0; } this->opened=1; this->current_dvd_device=intended_dvd_device; } dvdnav_get_title_string(this->dvdnav, &this->dvd_name); /* Set region code */ if (xine_config_lookup_entry (this->stream->xine, "input.dvd_region", ®ion_entry)) region_changed_cb (class, ®ion_entry); /* Set languages */ if (xine_config_lookup_entry (this->stream->xine, "input.dvd_language", &lang_entry)) language_changed_cb (class, &lang_entry); /* Set cache usage */ if (xine_config_lookup_entry(this->stream->xine, "input.dvd_use_readahead", &cache_entry)) read_ahead_cb(class, &cache_entry); /* Set seek mode */ if (xine_config_lookup_entry(this->stream->xine, "input.dvd_seek_behaviour", &cache_entry)) seek_mode_cb(class, &cache_entry); if(this->mode == MODE_TITLE) { int tt, i, pr, found; int titles, parts; /* A program and/or VTS was specified */ /* See if there is a period. */ found = -1; for(i=0; idvdnav, &titles); if((tt <= 0) || (tt > titles)) { printf("input_dvd: Title %i is out of range (1 to %i).\n", tt, titles); dvdnav_close(this->dvdnav); this->dvdnav = NULL; return 0; } /* If there was a part specified, get that too. */ pr = -1; if(found != -1) { pr = strtol(locator+found+1, NULL,10); } dvdnav_get_number_of_parts(this->dvdnav, tt, &parts); if ((pr == 0) || (pr > parts)) { printf("input_dvd: Part %i is out of range (1 to %i).\n", pr, parts); dvdnav_close(this->dvdnav); this->dvdnav = NULL; return 0; } #ifdef INPUT_DEBUG printf("input_dvd: Jumping to TT >%i<, PTT >%i<\n", tt, pr); #endif if(pr != -1) { dvdnav_part_play(this->dvdnav, tt, pr); } else { dvdnav_title_play(this->dvdnav, tt); } } #ifdef INPUT_DEBUG printf("input_dvd: DVD device successfully opened.\n"); #endif /* Tell Xine to update the UI */ event.type = XINE_EVENT_UI_CHANNELS_CHANGED; event.stream = this->stream; event.data = NULL; event.data_length = 0; xine_event_send(this->stream, &event); update_title_display(this); return 1; } /* dvdnav CLASS functions */ /* added by rwlin@avamax.com */ static int dvd_plugin_ioctl (input_plugin_t *this_gen,int cmd,void *data) { dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen; switch(cmd) { case DVDIOCTL_XINE_SPEED: dvdnav_set_speed(this->dvdnav,(int)data); break; case DVDIOCTL_GET_PARENTAL_LEVEL: dvdnav_get_parental_level(this->dvdnav,(int *)data); break; case DVDIOCTL_SET_PARENTAL_LEVEL: dvdnav_set_parental_level(this->dvdnav,(int)data); break; default: return -1; } return 0; } /* * Opens the DVD plugin. The MRL takes the following form: * * dvd:[dvd_path]/[vts[.program]] * * e.g. * dvd:/ - Play (navigate) * dvd:/1 - Play Title 1 * dvd:/1.3 - Play Title 1, program 3 * dvd:/dev/dvd2/ - Play (navigate) from /dev/dvd2 * dvd:/dev/dvd2/1.3 - Play Title 1, program 3 from /dev/dvd2 */ static input_plugin_t *dvd_class_get_instance (input_class_t *class_gen, xine_stream_t *stream, const char *data) { dvd_input_plugin_t *this; dvd_input_class_t *class = (dvd_input_class_t*)class_gen; static char *handled_mrl = "dvd:/"; trace_print("Called\n"); /* Check we can handle this MRL */ if (strncasecmp (data, handled_mrl, strlen(handled_mrl) ) != 0) return NULL; this = (dvd_input_plugin_t *) xine_xmalloc (sizeof (dvd_input_plugin_t)); if (this == NULL) { XINE_ASSERT(0, "input_dvd.c: xine_xmalloc failed!!!! You have run out of memory\n"); } this->input_plugin.open = dvd_plugin_open; this->input_plugin.get_capabilities = dvd_plugin_get_capabilities; 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.get_mrl = dvd_plugin_get_mrl; this->input_plugin.get_optional_data = dvd_plugin_get_optional_data; this->input_plugin.dispose = dvd_plugin_dispose; this->input_plugin.ioctl = dvd_plugin_ioctl; /* added by rwlin@avamax.com */ this->input_plugin.input_class = class_gen; this->stream = stream; this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HAS_STILL] = 1; this->dvdnav = NULL; this->opened = 0; this->seekable = 0; this->buttonN = 0; this->mouse_buttonN = -1; this->mouse_in = 0; this->typed_buttonN = 0; this->pause_timer = 0; this->pg_length = 0; this->pgc_length = 0; this->dvd_name = NULL; this->mrl = strdup(data); /* this->mrls = NULL; this->num_mrls = 0; */ pthread_mutex_init(&this->buf_mutex, NULL); this->mem_stack = 0; this->freeing = 0; this->event_queue = xine_event_new_queue (this->stream); /* config callbacks may react now */ class->ip = this; return &this->input_plugin; } static char *dvd_class_get_description (input_class_t *this_gen) { trace_print("Called\n"); return "DVD Navigator"; } static char *dvd_class_get_identifier (input_class_t *this_gen) { trace_print("Called\n"); return "DVD"; } /* FIXME: adapt to new api. */ #if 0 static xine_mrl_t **dvd_class_get_dir (input_class_t *this_gen, const char *filename, int *nFiles) { dvd_input_class_t *this = (dvd_input_class_t*)this_gen; trace_print("Called\n"); if (filename) { *nFiles = 0; return NULL; } /* dvd_build_mrl_list(this); *nFiles = this->num_mrls; return this->mrls; */ *nFiles = 0; return NULL; } #endif static char **dvd_class_get_autoplay_list (input_class_t *this_gen, int *num_files) { dvd_input_class_t *this = (dvd_input_class_t *) this_gen; trace_print("get_autoplay_list entered\n"); this->filelist2[0] = "dvd:/"; this->filelist2[1] = NULL; *num_files = 1; return this->filelist2; } static void dvd_class_dispose(input_class_t *this_gen) { dvd_input_class_t *this = (dvd_input_class_t*)this_gen; free(this->mrls); this->mrls = NULL; free(this); } static int dvd_class_eject_media (input_class_t *this_gen) { dvd_input_class_t *this = (dvd_input_class_t*)this_gen; return media_eject_media (this->dvd_device); } static void *init_class (xine_t *xine, void *data) { dvd_input_class_t *this; config_values_t *config = xine->config; void *dvdcss; static char *skip_modes[] = {"skip program", "skip part", "skip title", NULL}; static char *seek_modes[] = {"seek in program chain", "seek in program", NULL}; trace_print("Called\n"); #ifdef INPUT_DEBUG printf("input_dvd.c: init_class called.\n"); printf("input_dvd.c: config = %p\n", config); #endif this = (dvd_input_class_t *) malloc (sizeof (dvd_input_class_t)); this->input_class.get_instance = dvd_class_get_instance; this->input_class.get_identifier = dvd_class_get_identifier; this->input_class.get_description = dvd_class_get_description; /* this->input_class.get_dir = dvd_class_get_dir; */ this->input_class.get_dir = NULL; this->input_class.get_autoplay_list = dvd_class_get_autoplay_list; this->input_class.dispose = dvd_class_dispose; this->input_class.eject_media = dvd_class_eject_media; this->config = config; this->mrls = NULL; this->ip = NULL; /* this->num_mrls = 0; */ this->dvd_device = config->register_string(config, "input.dvd_device", DVD_PATH, "device used for dvd drive", NULL, 0, device_change_cb, (void *)this); if ((dvdcss = dlopen("libdvdcss.so.2", RTLD_LAZY)) != NULL) { /* we have found libdvdcss, enable the specific config options */ char *raw_device; static char *decrypt_modes[] = { "key", "disc", "title", NULL }; char *css_cache_default, *css_cache; int mode; raw_device = config->register_string(config, "input.dvd_raw_device", RDVD_PATH, "raw device set up for dvd access", NULL, 10, NULL, NULL); if (raw_device) xine_setenv("DVDCSS_RAW_DEVICE", raw_device, 0); mode = config->register_enum(config, "input.css_decryption_method", 0, decrypt_modes, "the css decryption method libdvdcss should use", NULL, 10, NULL, NULL); xine_setenv("DVDCSS_METHOD", decrypt_modes[mode], 0); css_cache_default = (char *)malloc(strlen(xine_get_homedir()) + 10); sprintf(css_cache_default, "%s/.dvdcss/", xine_get_homedir()); css_cache = config->register_string(config, "input.css_cache_path", css_cache_default, "path to the libdvdcss title key cache", NULL, 10, NULL, NULL); if (strlen(css_cache) > 0) xine_setenv("DVDCSS_CACHE", css_cache, 0); free(css_cache_default); dlclose(dvdcss); } 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 ", 0, region_changed_cb, this); config->register_string(config, "input.dvd_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.", 0, language_changed_cb, this); config->register_bool(config, "input.dvd_use_readahead", 1, "Do we use read-ahead caching?", "This " "may lead to jerky playback on low-end " "machines.", 10, read_ahead_cb, this); config->register_enum(config, "input.dvd_skip_behaviour", 0, skip_modes, "Skipping will work on this basis.", NULL, 10, NULL, NULL); config->register_enum(config, "input.dvd_seek_behaviour", 0, seek_modes, "Seeking will work on this basis.", NULL, 10, seek_mode_cb, this); #ifdef __sun check_solaris_vold_device(this); #endif #ifdef INPUT_DEBUG printf("input_dvd.c: init_class finished.\n"); #endif return this; } /* * $Log: input_dvd.c,v $ * Revision 1.11 2004/02/12 06:40:48 rwlin * fix the bug for INPUT_OPTIONAL_DATA_TITLES_CURRENT & INPUT_OPTIONAL_DATA_TITLES_CURRENT * * Revision 1.10 2004/01/28 05:23:51 rwlin * *** empty log message *** * * Revision 1.9 2003/12/22 13:27:22 rwlin * *** empty log message *** * * Revision 1.8 2003/12/22 12:37:36 kevin * Commit * * Revision 1.7 2003/12/09 07:33:59 georgedon * *** empty log message *** * * Revision 1.5 2003/12/05 04:06:40 rwlin * Recover back george's change in rev.1.2 * * Revision 1.4 2003/12/01 08:40:37 rwlin * *** empty log message *** * * Revision 1.168 2003/08/25 21:51:39 f1rmb * Reduce GCC verbosity (various prototype declaration fixes). ffmpeg, wine and fft*post are untouched (fft: for now). * * Revision 1.167 2003/08/21 00:37:26 miguelfreitas * RIP Input Plugin * * Revision 1.166 2003/06/29 10:57:08 mroi * on some DVDs, the first highlight information (which button to highlight) will * arrive before the first SPU packet, therefore the SPU decoder has not yet been * initialized; * we cannot just drop the highlight information when this happens, but we should * trigger decoder initialization by sending a dummy SPU packet * (this fixes wrong initial button highlight on "Star Trek DS9 Season 1") * * Revision 1.165 2003/06/02 06:36:32 f1rmb * new event which inform UI when the mouse pointer enter and leave a spu button (DVD navigation) * * Revision 1.164 2003/05/23 10:34:13 mroi * make alternative devices (dvd: and dvd: style MRLs) work with * raw devices configured * * problem is: The raw device setting is passed to libdvdcss through an environment * variable. Libdvdcss then replaces ANY read from anywhere with a read from the * raw device. This fails, when you want to play a disc image with dvd:, but * there is actually a DVD in the drive associated with the raw device which has * nothing to do with what you want to play. * * Revision 1.163 2003/05/16 15:07:36 tchamp * Fix win32 build and start adding additional plugin support * * Revision 1.162 2003/05/14 16:47:20 mroi * just to play it safe * * Revision 1.161 2003/05/07 17:54:18 tchamp * DVD play sort of works on Win32. Also added a couple more plugings to the Win32 build. * * Revision 1.160 2003/05/06 14:02:25 tchamp * This is some general Win32 cleanup and getting ready for DVD support. * * Revision 1.159 2003/05/03 14:24:08 mroi * as announced on xine-devel: * * I change the SPU decoder API to make it look less DVD specific * * adapt all related files * * increase SPU decoder API version * * include DVDNAV_CFLAGS locally where needed * * Revision 1.158 2003/04/30 16:41:15 mroi * the standalone libdvdnav can do raw device reads now, so this limitation * here is no longer necessary * * Revision 1.157 2003/04/29 15:58:28 jcdutton * Update from the libdvdnav project. * * Revision 1.156 2003/04/26 22:34:32 guenter * bump up input plugin interface version number * * Revision 1.155 2003/04/26 00:19:21 hadess * - shush * * Revision 1.154 2003/04/23 15:51:02 mroi * silence, please * * Revision 1.153 2003/04/22 23:30:29 tchamp * Additional changes for win32/msvc port; This is my first real commit so please be gentle with me; Everything builds except for the win32 ui * * Revision 1.152 2003/04/13 16:02:53 tmattern * Input plugin api change: * old open() function replaced by : * *_class_get_instance() : return an instance if the plugin handles the mrl * *_plugin_open() : open the stream * * Revision 1.151 2003/04/08 17:51:58 guenter * beta10 * * Revision 1.150 2003/04/08 13:58:11 mroi * fix compilation problems * * Revision 1.149 2003/04/07 18:13:19 mroi * support the new menu resume feature * * Revision 1.148 2003/04/07 16:51:29 mroi * output beautification * * Revision 1.147 2003/04/06 23:44:59 guenter * some more dvd error reporting * * Revision 1.146 2003/04/06 13:19:59 mroi * * fix input_time reporting for PG based seeking * (with more than one cell per PG, only the first cell starts at 0; for the others, * we need pg_start) * * check for title sanity * * fix tsble -> table typo * * Revision 1.145 2003/04/06 13:06:03 jcdutton * Enable display of DVD Menu types. * Currently needs libdvdnav cvs, but does not break xine's own libdvdnav version. * * Revision 1.144 2003/04/06 12:11:10 mroi * reset the VM when it is already open * * Revision 1.143 2003/04/06 00:51:29 hadess * - shared eject implementation taken from the DVD input, eject doesn't work if the CD/DVD isn't mounted, which definitely breaks the CDDA plugin... better than nothing * * Revision 1.142 2003/04/05 12:28:16 miguelfreitas * "perfect" time display for dvds * (see thread on xine-devel for details) * * Revision 1.141 2003/04/04 19:20:48 miguelfreitas * add initial async error/general message reporting to frontend * obs: more messages should be added * * Revision 1.140 2003/04/03 13:04:52 mroi * not so much noise in cvs * * Revision 1.139 2003/04/01 11:45:32 jcdutton * Fix race condition, where spudec_reset is called and then a button update arrives from input_dvd.c before we have our this->menu_handle back. * * Revision 1.138 2003/03/30 10:57:48 mroi * additional sanity check on the part number * * Revision 1.137 2003/03/29 13:19:08 mroi * sync to libdvdnav cvs once again * * some changes to mutual header inclusion to make it compile warning-less * when tracing is enabled * * title/part jumping should work much more reliable now * * Revision 1.136 2003/03/27 13:48:03 mroi * use timing information provided by libdvdnav to get more accurate position * * Revision 1.135 2003/03/25 13:20:31 mroi * new config option to switch between PG ("per chapter") and PGC ("per movie") * based seeking, * although this differs from the behaviour up to now, PGC based seeking is now the * default, since this is what people usually expect, what hardware players do and it * is needed for separate subtitles to work with DVDs. * * Revision 1.134 2003/03/13 22:09:51 mroi * turn these around so that dvd_get_current_position is defined before used * * Revision 1.133 2003/03/12 13:28:12 mroi * fix wrong return value of seek function, kindly reported by Nick Kurshev * * Revision 1.132 2003/03/04 10:30:28 mroi * fix compiler warnings at least in xine's native code * * Revision 1.131 2003/02/28 02:51:48 storri * Xine assert() replacement: * * All assert() function calls, with exceptions of libdvdread and libdvdnav, have been * replaced with XINE_ASSERT. Functionally XINE_ASSERT behaves just likes its predecesor but its * adding the ability to print out a stack trace at the point where the assertion fails. * So here are a few examples. * * assert (0); * * This use of assert was found in a couple locations most favorably being the default case of a switch * statement. This was the only thing there. So if the switch statement was unable to find a match * it would have defaulted to this and the user and the developers would be stuck wonder who died and where. * * So it has been replaced with * * XINE_ASSERT(0, "We have reach this point and don't have a default case"); * * It may seem a bit none descriptive but there is more going on behind the scene. * * In addition to checking a condition is true/false, in this case '0', the XINE_ASSERT * prints out: * * :: - assertion '' failed. * * An example of this might be: * * input_dvd.c:open_plugin:1178 - assertion '0' failed. xine_malloc failed!!! You have run out of memory * * XINE_ASSERT and its helper function, print_trace, are found in src/xine-utils/xineutils.h * * Revision 1.130 2003/02/26 20:45:18 mroi * adjust input_dvd to handle DVDNAV_WAIT events properly * (that is: wait for the fifos to become empty) * * Revision 1.129 2003/02/20 16:01:57 mroi * syncing to libdvdnav 0.1.5 and modifying input plugin accordingly * quoting the ChangeLog: * * some bugfixes * * code cleanup * * build process polishing * * more sensible event order in get_next_block to ensure useful event delivery * * VOBU level resume * * fixed: seeking in a multiangle feature briefly showed the wrong angle * * Revision 1.128 2003/02/14 18:00:38 heikos * FreeBSD compile fixes * * Revision 1.127 2003/02/13 16:24:27 mroi * use the requested channel number when querying for the language * (the _cool_ menu in xine-ui displays the correct languages now) * * Revision 1.126 2003/02/11 15:17:10 mroi * enable libdvdcss title key cache * * Revision 1.125 2002/12/27 16:47:10 miguelfreitas * man errno: "must not be explicitly declared; errno may be a macro" * (thanks Chris Rankin for noticing) * * Revision 1.124 2002/12/22 23:35:42 miguelfreitas * it doesn't make sense to reimplement flush here. * (this is why xine_demux_flush_engine was created, to avoid redundant code) * * Revision 1.123 2002/12/21 12:56:47 miguelfreitas * - add buf->decoder_info_ptr: portability for systems where pointer has * different sizeof than integer. * - add extra_info structure to pass informations from input/demuxers down * to the output frame. this can be used, for example, to pass the frame * number of a frame (when known by decoder). also, immediate benefict is * that we now have a slider which really shows the current position of * the playing stream. new fields can be added to extra_info keeping * binary compatibility * - bumpy everybody's api versions * * Revision 1.122 2002/12/06 18:44:40 miguelfreitas * - add still frame hint (untested - i don't have dvd here) * - check mrl before allocating plugin context, so it doesn't get initialized for * non-dvd streams * * Revision 1.121 2002/11/23 12:41:04 mroi * DVD input fixes and cleanup: * * revert my removing of the clock adjustment; although this is bad, it seems * to be the best solution for now (menu transitions have choppy audio without) * * add patch from Marco Zühlke enabling dvd device specification by MRL * * update GUI title and language display once immediately after plugin open * * Revision 1.120 2002/11/23 11:09:29 f1rmb * registering config entries at init_class time * * Revision 1.119 2002/11/22 16:23:58 mroi * do not play with the clock any more, we have dedicated flush functions for that now * (This should fix Daniels MP3 problems, since the end of one stream would * have adjusted the global clock thus affecting all other streams.) * * Revision 1.118 2002/11/20 11:57:42 mroi * engine modifications to allow post plugin layer: * * new public output interface xine_{audio,video}_port_t instead of * xine_{ao,vo}_driver_t, old names kept as aliases for compatibility * * modified the engine to allow multiple streams per output * * renaming of some internal structures according to public changes * * moving SCR out of per-stream-metronom into a global metronom_clock_t * residing in xine_t and therefore easily available to the output layer * * adapting all available plugins * (note to external projects: the compiler will help you a lot, if a plugin * compiles, it is adapted, because all changes add new parameters to some * functions) * * bump up all interface versions because of xine_t and xine_stream_t changes * * Revision 1.117 2002/11/18 11:48:35 mroi * DVD input should now be initially unseekable * * Revision 1.116 2002/11/18 11:33:59 mroi * getting rid of obviously unused INPUT_CAP_VARIABLE_BITRATE * fix ejecting (works now) * * Revision 1.115 2002/11/17 16:23:38 mroi * cleanup: bring config entries back to life * introduce a seekable flag * * Revision 1.114 2002/11/15 00:20:32 miguelfreitas * cleaning up spu types. now avi subtitles may be enabled again. * (+ missed ffmpeg/dv patch) * * Revision 1.113 2002/11/03 23:03:31 siggi * some more release-related fixes... * * Revision 1.112 2002/11/02 15:13:01 mroi * don't display crap in UI panel, xine-ui expects a xine_ui_data_t and * I think this is right, so we provide one * * Revision 1.111 2002/11/02 03:13:44 f1rmb * Less verbosity. * * Revision 1.110 2002/11/01 17:51:57 mroi * be less strict with MRL syntax, people are used to :// * * Revision 1.109 2002/11/01 11:48:59 tmattern * Time for fast navigation now ! * * Revision 1.108 2002/10/31 17:00:45 mroi * adapt input plugins to new MRL syntax * (mostly turning :// into :/) * * Revision 1.107 2002/10/27 20:07:39 mroi * less noise and register skip_behaviour (chapter skip keys work again) * * Revision 1.106 2002/10/26 22:50:52 guenter * timeouts for mms, send progress report events, introduce verbosity engine parameter (not implemented yet), document new plugin loader in changelog * * Revision 1.105 2002/10/26 20:15:21 mroi * first step in getting dvd events back * * Revision 1.104 2002/10/26 02:12:27 jcdutton * Remove assert(0), left over from testing. * dispose of event queue. * * Revision 1.103 2002/10/25 15:36:19 mroi * remove obviously obsolete INPUT_CAP_CLUT and INPUT_OPTIONAL_DATA_CLUT * * Revision 1.102 2002/10/24 15:06:55 jkeil * C99 version of macro definition with variable number of arguments added * * Revision 1.101 2002/10/24 13:52:57 jcdutton * Fix some log messages in audio_alsa_out.c * Fix input_dvd.c for new config file loading before init_class(). * * Revision 1.100 2002/10/24 11:30:38 jcdutton * Further changes to DVD code. * * Revision 1.99 2002/10/23 20:26:34 guenter * final c++ -> c coding style fixes, libxine compiles now * * Revision 1.98 2002/10/23 11:59:52 jcdutton * Oops...will compile now. * * Revision 1.97 2002/10/23 11:44:31 jcdutton * input_dvd.c now listens for keyboard events from xine-ui. * * Revision 1.96 2002/10/23 10:14:08 jkeil * "dvd_device" device name moved from dvd_input_plugin_t -> dvd_input_class_t, * adapt the check_solaris_vold_device() function. * * Revision 1.95 2002/10/22 17:16:57 jkeil * Fix bad comment, and disable some piece of code to enable compilation on solaris * * Revision 1.94 2002/10/22 07:36:05 jcdutton * Update input_dvd.c to new api. * Plays DVDs now, but not menu buttons work yet. * * Revision 1.93 2002/10/14 15:47:16 guenter * introduction of xine_stream_t and async xine events - all still in developement * * Revision 1.92 2002/10/06 15:48:02 jkeil * Proper alignment is needed for the array of "xine_mrl_t" structures on SPARC. * * Revision 1.91 2002/10/02 15:56:51 mroi * - kill global variables * - remove some code that could never be reached (after return) * * Revision 1.90 2002/09/28 11:10:04 mroi * configurable skipping behaviour * * Revision 1.89 2002/09/22 14:29:40 mroi * API review part I * - bring our beloved xine_t * back (no more const there) * - remove const on some input plugin functions * where the data changes with media (dvd, ...) changes * and is therefore not const * * Revision 1.86 2002/09/18 10:03:07 jcdutton * Fix a seg fault. * * Revision 1.85 2002/09/18 06:42:23 jcdutton * Try to get xine-lib to compile. * * Revision 1.84 2002/09/18 04:20:09 jcdutton * Updating the DVD menu code to use better nav_pci information. * libspudec parses nav_pci info correctly. * libdvdnav does not parse nav_pci info at all. * * Revision 1.83 2002/09/17 07:53:59 jcdutton * Make input_dvd.c mrl playlist work again. * * Revision 1.82 2002/09/16 16:55:35 jcdutton * Start to get mrl working for DVD button. * * Revision 1.81 2002/09/16 16:13:56 jcdutton * Prevent a segfault when accessing the config. * * Revision 1.80 2002/09/15 14:05:37 mroi * be more distinct with UI info texts for * "no subtitles because user switched it off" * and * "no subtitles because none are available" * * Revision 1.79 2002/09/14 19:04:07 guenter * latest xine_config api changes as proposed by james * * Revision 1.78 2002/09/13 17:18:42 mroi * dvd playback should work again * * Revision 1.77 2002/09/06 18:13:10 mroi * introduce "const" * fix some input plugins that would not copy the mrl on open * * Revision 1.76 2002/09/05 22:18:54 mroi * remove plugin's private priority and interface members * adapt some more decoders * * Revision 1.75 2002/09/05 20:44:39 mroi * make all the plugin init functions static * (geez this was a job) * * Revision 1.74 2002/09/05 20:19:48 guenter * use xine_mrl_t instead of mrl_t in input plugins, implement more configfile functions * * Revision 1.73 2002/09/05 05:51:14 jcdutton * XV Video out at least loads now and we see the xine logo again. * The DVD plugin now loads, but audio and spu info is lost. * What happened to xine_get_spu_channel and xine_get_audio_channel? * * Revision 1.72 2002/09/04 23:31:08 guenter * merging in the new_api branch ... unfortunately video_out / vo_scale is broken now ... matthias/miguel: please fix it :-) * * Revision 1.71 2002/09/04 10:48:36 mroi * - handle numeric events for button selection (maybe this makes some * dvd's easter eggs accesible) * - workaround current breakage in libdvdnav concerning mrl list building * * Revision 1.70 2002/09/03 07:51:34 jcdutton * Improve chapter selection functions. * * Revision 1.69 2002/09/02 12:25:49 jcdutton * This might slow things down a bit, but I need to do it to test a problem with DVD menus * not appearing. * I think the reason they are not appearing is that they are getting flushed too early. * * Revision 1.68 2002/09/02 03:21:38 jcdutton * Implement proper prev/next chapter. * * Revision 1.67 2002/08/31 02:48:13 jcdutton * Add a printf so we can tell if a user is using xine's libdvdnav or the one from * dvd.sf.net. * Add some "this->dvdnav = NULL;" after dvd_close() * * Revision 1.66 2002/08/30 11:14:44 mroi * make menu key output conform xine guidelines, improve compatibility with * older xine-ui versions by handling XINE_EVENT_INPUT_MENU1 * * Revision 1.65 2002/08/29 04:32:12 jcdutton * Use more Fkeys to jump to different DVD menus. * We can now jump directly to Title, Root, Sub-Picture, Audio, Angle, PTT (Chapter) menus. * * Revision 1.64 2002/08/26 11:50:47 mroi * adapt to xine coding guidelines * * Revision 1.63 2002/08/21 23:38:48 komadori * fix portability problems * * Revision 1.62 2002/08/21 15:10:09 mroi * use raw devices only with our patched local copy of libdvdread * * Revision 1.61 2002/08/19 17:27:11 mroi * add config entries for raw device and css decryption method * * Revision 1.60 2002/08/13 16:04:27 jkeil * Solaris uses for CDROM/DVD-ROM ioctl, too. Try to use autoconf * HAVE_headerfile macros... (The xxxBSD part nees a bit work) * * Revision 1.59 2002/08/13 15:55:23 mroi * change error to warning * * Revision 1.58 2002/08/09 22:33:10 mroi * sorry, my raw device patch was not meant to be committed * It only works with a patched version of libdvdcss * * Revision 1.57 2002/08/09 22:13:08 mroi * make developers life easier: add possibility to use an existing shared * version of libdvdnav * * Revision 1.56 2002/08/09 15:38:13 mroi * fix mrl parsing * * Revision 1.55 2002/08/09 13:50:17 heikos * seems to compile better this way :) * * Revision 1.54 2002/08/09 07:34:47 richwareham * More include fixes * * Revision 1.53 2002/08/08 17:49:21 richwareham * First stage of DVD plugin -> dvdnav conversion * */ plugin_info_t xine_plugin_info[] = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_INPUT, 13, "DVD", XINE_VERSION_CODE, NULL, init_class }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } };