1 /*
2  * Copyright (C) 2000-2019 the xine project,
3  *                         Rich Wareham <richwareham@users.sourceforge.net>
4  *
5  * This file is part of xine, a free video player.
6  *
7  * xine is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * xine is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
20  */
21 
22 /* This file was origninally part of the xine-dvdnav project
23  * at http://dvd.sf.net/.
24  */
25 
26 /* TODO:
27  *
28  *  - Proper internationalisation of strings.
29  *  - Failure dialogue.
30  */
31 
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 
36 /* Standard includes */
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <stddef.h>
40 
41 #ifndef WIN32
42 #include <sys/param.h>
43 #endif /* WIN32 */
44 
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 
48 #include <unistd.h>
49 #include <pthread.h>
50 #include <fcntl.h>
51 #include <time.h>
52 #include <string.h>
53 #include <errno.h>
54 #include <dlfcn.h>
55 
56 #ifndef WIN32
57 #if ! defined(__GNU__)
58 #include <sys/mount.h>
59 #endif
60 #include <sys/wait.h>
61 
62 #include <sys/poll.h>
63 #include <sys/ioctl.h>
64 #endif /* WIN32 */
65 
66 
67 #if defined(HAVE_LINUX_CDROM_H)
68 #include <linux/cdrom.h>
69 #elif defined(HAVE_SYS_DVDIO_H)
70 #include <sys/dvdio.h>
71 #include <sys/cdio.h> /* CDIOCALLOW etc... */
72 #elif defined(HAVE_SYS_CDIO_H)
73 #include <sys/cdio.h>
74 #elif defined(WIN32)
75 #include <io.h>                                                 /* read() */
76 #else
77 #warning "This might not compile due to missing cdrom ioctls"
78 #endif
79 
80 /* DVDNAV includes */
81 #ifdef HAVE_DVDNAV
82 #  include <dvdnav/dvdnav.h>
83 #  ifdef HAVE_DVDNAV_NAVTYPES_H
84 #    include <dvdnav/nav_types.h>
85 #    include <dvdnav/nav_read.h>
86 #  else
87 #    include <dvdread/nav_types.h>
88 #    include <dvdread/nav_read.h>
89 #  endif
90 #else
91 #  define DVDNAV_COMPILE
92 #  include "libdvdnav/dvdnav.h"
93 #  include "libdvdnav/nav_read.h"
94 #endif
95 
96 #define LOG_MODULE "input_http"
97 #define LOG_VERBOSE
98 /*
99 #define LOG
100 */
101 
102 /* Xine includes */
103 #include <xine/xineutils.h>
104 #include <xine/buffer.h>
105 #include <xine/xine_internal.h>
106 #include "media_helper.h"
107 
108 /* Print debug messages? */
109 /* #define INPUT_DEBUG */
110 
111 /* Current play mode (title only or menus?) */
112 #define MODE_FAIL	0
113 #define MODE_NAVIGATE	1
114 #define MODE_TITLE	2
115 
116 /* Is seeking enabled? 1 - Yes, 0 - No */
117 #define CAN_SEEK 1
118 
119 /* The default DVD device on Solaris is not /dev/dvd */
120 #if defined(__sun)
121 #define DVD_PATH "/vol/dev/aliases/cdrom0"
122 #define RDVD_PATH ""
123 #elif WIN32
124 /* There really isn't a default on Windows! */
125 #define DVD_PATH "d:\\"
126 #define RDVD_PATH "d:\\"
127 #elif defined(__OpenBSD__)
128 #define DVD_PATH "/dev/rcd0c"
129 #define RDVD_PATH "/dev/rcd0c"
130 #else
131 #define DVD_PATH "/dev/dvd"
132 #define RDVD_PATH "/dev/rdvd"
133 #endif
134 
135 /* Some misc. defines */
136 #ifdef DVD_VIDEO_LB_LEN
137 #  define DVD_BLOCK_SIZE DVD_VIDEO_LB_LEN
138 #else
139 #  define DVD_BLOCK_SIZE 2048
140 #endif
141 
142 #if defined (__FreeBSD__)
143 # define off64_t off_t
144 # define lseek64 lseek
145 #endif
146 
147 static const char *const dvdnav_menu_table[] = {
148   NULL,
149   NULL,
150   "Title",
151   "Root",
152   "Subpicture",
153   "Audio",
154   "Angle",
155   "Part"
156 };
157 
158 typedef struct dvd_input_plugin_s dvd_input_plugin_t;
159 
160 typedef union dvd_input_saved_buf_u {
161   union dvd_input_saved_buf_u *free_next;
162   struct {
163     dvd_input_plugin_t *this;
164     unsigned char      *block;
165     void               *source;
166     void              (*free_buffer) (buf_element_t *);
167   } used;
168 } dvd_input_saved_buf_t;
169 
170 struct dvd_input_plugin_s {
171   input_plugin_t    input_plugin; /* Parent input plugin type        */
172 
173   xine_stream_t    *stream;
174   xine_event_queue_t *event_queue;
175 
176   int               pause_timer;  /* Cell still-time timer            */
177   int               pause_counter;
178   time_t	    pause_end_time;
179   int64_t           pg_length;
180   int64_t           pgc_length;
181   int64_t           cell_start;
182   int64_t           pg_start;
183   int32_t           buttonN;
184   int               typed_buttonN;/* for XINE_EVENT_INPUT_NUMBER_* */
185 
186   int32_t           mouse_buttonN;
187   int               mouse_in;
188 
189   /* Flags */
190   int               opened;       /* 1 if the DVD device is already open */
191   int               seekable;     /* are we seekable? */
192   int               mode;         /* MODE_NAVIGATE / MODE_TITLE */
193   int               tt, pr;       /* title / chapter */
194 
195   /* xine specific variables */
196   char             *mrl;          /* Current MRL                     */
197   dvdnav_t         *dvdnav;       /* Handle for libdvdnav            */
198   const char       *dvd_name;
199 
200   /* parsed mrl */
201   char             *device; /* DVD device currently open */
202   int               title;
203   int               part;
204 
205   /* special buffer handling for libdvdnav caching */
206   pthread_mutex_t   buf_mutex;
207   dvd_input_saved_buf_t *saved_bufs;
208   dvd_input_saved_buf_t *saved_free;
209   unsigned int      saved_used;
210   unsigned int      saved_size;
211 
212   uint32_t          user_conf_version;
213   int32_t           user_read_ahead;
214   int32_t           user_seek_mode;
215   int32_t           user_region;
216   char              user_lang4[4];
217 
218   int               freeing;
219 };
220 
221 typedef struct {
222 
223   input_class_t       input_class;
224 
225   xine_t             *xine;
226 
227   const char         *dvd_device;    /* default DVD device */
228   char               *eject_device;  /* the device last opened is remembered for eject */
229 
230   uint32_t            user_conf_version;
231   int32_t             user_read_ahead;
232   int32_t             user_seek_mode;
233   int32_t             user_region;
234   char                user_lang4[4];
235   int32_t             user_play_single_chapter;
236   int32_t             user_skip_mode;
237 
238 } dvd_input_class_t;
239 
dvd_input_saved_new(dvd_input_plugin_t * this)240 static int dvd_input_saved_new (dvd_input_plugin_t *this) {
241   unsigned int n;
242   dvd_input_saved_buf_t *s = malloc (1024 * sizeof (*s));
243   if (!s)
244     return 1;
245   this->saved_bufs =
246   this->saved_free = s;
247   for (n = 1024 - 1; n; n--) {
248     s++;
249     s[-1].free_next = s;
250   }
251   s[0].free_next   = NULL;
252   this->saved_used = 0;
253   this->saved_size = 1024;
254   return 0;
255 }
256 
dvd_input_saved_delete(dvd_input_plugin_t * this)257 static void dvd_input_saved_delete (dvd_input_plugin_t *this) {
258   _x_freep (&this->saved_bufs);
259 }
260 
261 /* Get this->buf_mutex for the next 2. */
dvd_input_saved_acquire(dvd_input_plugin_t * this)262 static dvd_input_saved_buf_t *dvd_input_saved_acquire (dvd_input_plugin_t *this) {
263   dvd_input_saved_buf_t *s = this->saved_free;
264   if (s) {
265     this->saved_free = s->free_next;
266     this->saved_used++;
267     s->used.this = this;
268   }
269   return s;
270 }
271 
dvd_input_saved_release(dvd_input_plugin_t * this,dvd_input_saved_buf_t * s)272 static int dvd_input_saved_release (dvd_input_plugin_t *this, dvd_input_saved_buf_t *s) {
273   s->free_next = this->saved_free;
274   this->saved_free = s;
275   this->saved_used--;
276   return this->saved_used;
277 }
278 
279 
280 static void dvd_handle_events(dvd_input_plugin_t *this);
281 static void xine_dvd_send_button_update(dvd_input_plugin_t *this, int mode);
282 
283 /* Callback on device name change */
device_change_cb(void * data,xine_cfg_entry_t * cfg)284 static void device_change_cb(void *data, xine_cfg_entry_t *cfg) {
285   dvd_input_class_t *class = (dvd_input_class_t *) data;
286 
287   class->dvd_device = cfg->str_value;
288 }
289 
dvd_plugin_get_capabilities(input_plugin_t * this_gen)290 static uint32_t dvd_plugin_get_capabilities (input_plugin_t *this_gen) {
291   dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen;
292 
293   lprintf("Called\n");
294 
295   return INPUT_CAP_BLOCK |
296   /* TODO: figure out if there is any "allow copying" flag on DVD.
297    *       maybe set INPUT_CAP_RIP_FORBIDDEN only for encrypted media?
298    */
299     INPUT_CAP_RIP_FORBIDDEN |
300 #if CAN_SEEK
301     (this->seekable ? INPUT_CAP_SEEKABLE : 0) |
302 #endif
303     INPUT_CAP_AUDIOLANG | INPUT_CAP_SPULANG | INPUT_CAP_CHAPTERS;
304 }
305 
apply_cfg(dvd_input_plugin_t * this)306 static void apply_cfg (dvd_input_plugin_t *this) {
307   dvd_input_class_t *class = (dvd_input_class_t*)this->input_plugin.input_class;
308 
309   this->user_conf_version  = class->user_conf_version;
310   this->user_read_ahead    = class->user_read_ahead;
311   this->user_seek_mode     = class->user_seek_mode;
312   this->user_region        = class->user_region;
313   memcpy (this->user_lang4, class->user_lang4, 4);
314 
315   dvdnav_set_readahead_flag       (this->dvdnav, this->user_read_ahead);
316   dvdnav_set_PGC_positioning_flag (this->dvdnav, !this->user_seek_mode);
317   dvdnav_set_region_mask          (this->dvdnav, 1 << (this->user_region - 1));
318   dvdnav_menu_language_select     (this->dvdnav, this->user_lang4);
319   dvdnav_audio_language_select    (this->dvdnav, this->user_lang4);
320   dvdnav_spu_language_select      (this->dvdnav, this->user_lang4);
321 }
322 
update_cfg(dvd_input_plugin_t * this)323 static void update_cfg (dvd_input_plugin_t *this) {
324   dvd_input_class_t *class = (dvd_input_class_t*)this->input_plugin.input_class;
325   if (class->user_read_ahead != this->user_read_ahead) {
326     this->user_read_ahead = class->user_read_ahead;
327     dvdnav_set_readahead_flag (this->dvdnav, this->user_read_ahead);
328   }
329   if (class->user_seek_mode != this->user_seek_mode) {
330     this->user_seek_mode = class->user_seek_mode;
331     dvdnav_set_PGC_positioning_flag (this->dvdnav, !this->user_seek_mode);
332   }
333   if (class->user_region != this->user_region) {
334     this->user_region = class->user_region;
335     dvdnav_set_region_mask (this->dvdnav, 1 << (this->user_region - 1));
336   }
337   if (memcmp (class->user_lang4, this->user_lang4, 4)) {
338     memcpy (this->user_lang4, class->user_lang4, 4);
339     dvdnav_menu_language_select (this->dvdnav, this->user_lang4);
340     dvdnav_audio_language_select (this->dvdnav, this->user_lang4);
341     dvdnav_spu_language_select (this->dvdnav, this->user_lang4);
342   }
343 }
344 
read_ahead_cb(void * this_gen,xine_cfg_entry_t * entry)345 static void read_ahead_cb(void *this_gen, xine_cfg_entry_t *entry) {
346   dvd_input_class_t *class = (dvd_input_class_t*)this_gen;
347   if (class) {
348     class->user_conf_version += 1;
349     class->user_read_ahead = entry->num_value;
350   }
351 }
352 
seek_mode_cb(void * this_gen,xine_cfg_entry_t * entry)353 static void seek_mode_cb(void *this_gen, xine_cfg_entry_t *entry) {
354   dvd_input_class_t *class = (dvd_input_class_t*)this_gen;
355   if (class) {
356     class->user_conf_version += 1;
357     class->user_seek_mode = entry->num_value;
358   }
359 }
360 
region_changed_cb(void * this_gen,xine_cfg_entry_t * entry)361 static void region_changed_cb (void *this_gen, xine_cfg_entry_t *entry) {
362   dvd_input_class_t *class = (dvd_input_class_t*)this_gen;
363   if (class && (entry->num_value >= 1) && (entry->num_value <= 8)) {
364     class->user_conf_version += 1;
365     class->user_region = entry->num_value;
366   }
367 }
368 
language_changed_cb(void * this_gen,xine_cfg_entry_t * entry)369 static void language_changed_cb(void *this_gen, xine_cfg_entry_t *entry) {
370   dvd_input_class_t *class = (dvd_input_class_t*)this_gen;
371   if (class && entry->str_value) {
372     class->user_conf_version += 1;
373     strlcpy (class->user_lang4, entry->str_value, 4);
374   }
375 }
376 
play_single_chapter_cb(void * this_gen,xine_cfg_entry_t * entry)377 static void play_single_chapter_cb(void *this_gen, xine_cfg_entry_t *entry) {
378   dvd_input_class_t *class = (dvd_input_class_t*)this_gen;
379 
380   if(!class)
381    return;
382 
383   class->user_play_single_chapter = entry->num_value;
384 }
385 
skip_changed_cb(void * this_gen,xine_cfg_entry_t * entry)386 static void skip_changed_cb (void *this_gen, xine_cfg_entry_t *entry) {
387   dvd_input_class_t *class = (dvd_input_class_t*)this_gen;
388   if (class)
389     class->user_skip_mode = entry->num_value;
390 }
391 
send_mouse_enter_leave_event(dvd_input_plugin_t * this,int direction)392 static void send_mouse_enter_leave_event(dvd_input_plugin_t *this, int direction) {
393 
394   if(direction && this->mouse_in)
395     this->mouse_in = !this->mouse_in;
396 
397   if(direction != this->mouse_in) {
398     xine_spu_button_t spu_event = {
399       .direction = direction,
400       .button    = this->mouse_buttonN
401     };
402     xine_event_t event;
403     event.type        = XINE_EVENT_SPU_BUTTON;
404     event.stream      = this->stream;
405     event.data        = &spu_event;
406     event.data_length = sizeof(spu_event);
407     xine_event_send(this->stream, &event);
408 
409     this->mouse_in = direction;
410   }
411 
412   if(!direction)
413     this->mouse_buttonN = -1;
414 }
415 
update_title_display(dvd_input_plugin_t * this)416 static int update_title_display(dvd_input_plugin_t *this) {
417   xine_ui_data_t data;
418   xine_event_t uevent = {
419     .type = XINE_EVENT_UI_SET_TITLE,
420     .stream = this->stream,
421     .data = &data,
422     .data_length = sizeof(data)
423   };
424   int tt=-1, pr=-1;
425   int num_tt = 0;
426 
427   /* Set title/chapter display */
428 
429   dvdnav_current_title_info(this->dvdnav, &tt, &pr);
430   if( this->mode == MODE_TITLE ) {
431     if( (((dvd_input_class_t *)this->input_plugin.input_class)->user_play_single_chapter ) ) {
432       if( (this->tt && this->tt != tt) ||
433           (this->pr && this->pr != pr) )
434         return 0;
435     }
436     this->tt = tt;
437     this->pr = pr;
438   }
439 
440   dvdnav_get_number_of_titles(this->dvdnav, &num_tt );
441 
442 
443   if(tt >= 1) {
444     int num_angle = 0, cur_angle = 0;
445     int num_part = 0;
446     /* no menu here */
447     /* Reflect angle info if appropriate */
448     dvdnav_get_number_of_parts(this->dvdnav, tt, &num_part);
449     dvdnav_get_angle_info(this->dvdnav, &cur_angle, &num_angle);
450     if(num_angle > 1) {
451       data.str_len = snprintf(data.str, sizeof(data.str),
452 			       "Title %i, Chapter %i, Angle %i of %i",
453 			       tt,pr,cur_angle, num_angle);
454        _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_ANGLE_NUMBER,cur_angle);
455        _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_ANGLE_COUNT,num_angle);
456     } else {
457       data.str_len = snprintf(data.str, sizeof(data.str),
458 			       "Title %i, Chapter %i",
459 			       tt,pr);
460        _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_ANGLE_NUMBER,0);
461        _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_ANGLE_COUNT,0);
462     }
463     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_TITLE_NUMBER,tt);
464     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_TITLE_COUNT,num_tt);
465     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_CHAPTER_NUMBER,pr);
466     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_CHAPTER_COUNT,num_part);
467   } else if (tt == 0 && dvdnav_menu_table[pr]) {
468     data.str_len = snprintf(data.str, sizeof(data.str),
469 			     "DVD %s Menu",
470 			     dvdnav_menu_table[pr]);
471      _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_TITLE_NUMBER,tt);
472      _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_TITLE_COUNT,num_tt);
473      _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_CHAPTER_NUMBER,0);
474      _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_CHAPTER_COUNT,0);
475      _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_ANGLE_NUMBER,0);
476      _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_ANGLE_COUNT,0);
477   } else {
478     strcpy(data.str, "DVD Menu");
479     data.str_len = strlen(data.str);
480     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_TITLE_NUMBER,0);
481     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_TITLE_COUNT,num_tt);
482     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_CHAPTER_NUMBER,0);
483     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_CHAPTER_COUNT,0);
484     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_ANGLE_NUMBER,0);
485     _x_stream_info_set(this->stream,XINE_STREAM_INFO_DVD_ANGLE_COUNT,0);
486   }
487 
488   if (this->dvd_name && this->dvd_name[0] &&
489       (data.str_len + strlen(this->dvd_name) < sizeof(data.str))) {
490     data.str_len += snprintf(data.str+data.str_len, sizeof(data.str) - data.str_len,
491 			      ", %s", this->dvd_name);
492   }
493 #ifdef INPUT_DEBUG
494   printf("input_dvd: Changing title to read '%s'\n", data.str);
495 #endif
496   xine_event_send(this->stream, &uevent);
497 
498   return 1;
499 }
500 
dvd_plugin_dispose(input_plugin_t * this_gen)501 static void dvd_plugin_dispose (input_plugin_t *this_gen) {
502   dvd_input_plugin_t *this = (dvd_input_plugin_t *)this_gen;
503 
504   lprintf("Called\n");
505 
506   if (this->event_queue)
507     xine_event_dispose_queue (this->event_queue);
508 
509   pthread_mutex_lock (&this->buf_mutex);
510   if (this->saved_used) {
511     /* raise the freeing flag, so that the plugin will be freed as soon
512      * as all buffers have returned to the libdvdnav read ahead cache */
513     this->freeing = 1;
514     pthread_mutex_unlock (&this->buf_mutex);
515   } else {
516     pthread_mutex_unlock (&this->buf_mutex);
517     pthread_mutex_destroy (&this->buf_mutex);
518     if (this->dvdnav) {
519       dvdnav_close (this->dvdnav);
520       this->dvdnav = NULL;
521     }
522     dvd_input_saved_delete (this);
523     _x_freep (&this->device);
524     _x_freep (&this->mrl);
525     free (this);
526   }
527 }
528 
529 
530 /* Align pointer |p| to alignment |align| */
531 #define PTR_ALIGN(p, align) ((void*)(((uintptr_t)(p) + (align) - 1) & ~((uintptr_t)(align)-1)))
532 
533 
534 /* FIXME */
535 #if 0
536 static void dvd_build_mrl_list(dvd_input_plugin_t *this) {
537   int num_titles, *num_parts;
538 
539   /* skip DVD if already open */
540   if (this->opened) return;
541   if (this->class->mrls) {
542     free(this->class->mrls);
543     this->class->mrls = NULL;
544     this->class->num_mrls = 0;
545   }
546 
547   if (dvdnav_open(&(this->dvdnav),
548 		  this->dvd_device) == DVDNAV_STATUS_ERR) {
549     return;
550   }
551 
552   this->current_dvd_device = this->dvd_device;
553   this->opened = 1;
554 
555   dvdnav_get_number_of_titles(this->dvdnav, &num_titles);
556   if ((num_parts = (int *) calloc(num_titles, sizeof(int)))) {
557     struct xine_mrl_align_s {
558       char dummy;
559       xine_mrl_t mrl;
560     };
561     int xine_mrl_alignment = offsetof(struct xine_mrl_align_s, mrl);
562     int num_mrls = 1, i;
563     /* for each title, count the number of parts */
564     for (i = 1; i <= num_titles; i++) {
565       num_parts[i-1] = 0;
566       /* dvdnav_title_play(this->dvdnav, i); */
567       dvdnav_get_number_of_parts(this->dvdnav, i, &num_parts[i-1]);
568       num_mrls += num_parts[i-1]; /* num_mrls = total number of programs */
569     }
570 
571     /* allocate enough memory for:
572      * - a list of pointers to mrls       sizeof(xine_mrl_t *)     * (num_mrls+1)
573      * - possible alignment of the mrl array
574      * - an array of mrl structures       sizeof(xine_mrl_t)       * num_mrls
575      * - enough chars for every filename  sizeof(char)*25     * num_mrls
576      *   - "dvd:/000000.000000\0" = 25 chars
577      */
578     if ((this->mrls = (xine_mrl_t **) malloc(sizeof(xine_mrl_t *) + num_mrls *
579 	(sizeof(xine_mrl_t*) + sizeof(xine_mrl_t) + 25*sizeof(char)) +
580 	xine_mrl_alignment))) {
581 
582       /* the first mrl struct comes after the pointer list */
583       xine_mrl_t *mrl = PTR_ALIGN(&this->mrls[num_mrls+1], xine_mrl_alignment);
584 
585       /* the chars for filenames come after the mrl structs */
586       char *name = (char *) &mrl[num_mrls];
587       int pos = 0, j;
588       this->num_mrls = num_mrls;
589 
590       for (i = 1; i <= num_titles; i++) {
591 	for (j = (i == 1 ? 0 : 1); j <= num_parts[i-1]; j++) {
592 	  this->class->mrls[pos++] = mrl;
593 	  mrl->origin = NULL;
594 	  mrl->mrl = name;
595 	  mrl->link = NULL;
596 	  mrl->type = mrl_dvd;
597 	  mrl->size = 0;
598 	  snprintf(name, 25, (j == 0) ? "dvd:/" :
599 		             (j == 1) ? "dvd:/%d" :
600 		                        "dvd:/%d.%d", i, j);
601 	  name = &name[25];
602 	  mrl++;
603 	}
604       }
605       this->class->mrls[pos] = NULL; /* terminate list */
606     }
607     free(num_parts);
608   }
609 }
610 #endif
611 
dvd_plugin_free_buffer(buf_element_t * buf)612 static void dvd_plugin_free_buffer (buf_element_t *buf) {
613   dvd_input_saved_buf_t *s = buf->source;
614   dvd_input_plugin_t *this = s->used.this;
615   unsigned int n;
616 
617   pthread_mutex_lock (&this->buf_mutex);
618   /* reconstruct the original xine buffer */
619   buf->free_buffer = s->used.free_buffer;
620   buf->source = s->used.source;
621   /* give this buffer back to libdvdnav */
622   dvdnav_free_cache_block (this->dvdnav, s->used.block);
623   s->used.block = NULL;
624   /* free saved entry */
625   n = dvd_input_saved_release (this, s);
626   pthread_mutex_unlock (&this->buf_mutex);
627   /* give this buffer back to xine's pool */
628   buf->free_buffer (buf);
629   if (this->freeing && !n) {
630     /* all buffers returned, we can free the plugin now */
631     pthread_mutex_destroy (&this->buf_mutex);
632     if (this->dvdnav) {
633       dvdnav_close (this->dvdnav);
634       this->dvdnav = NULL;
635     }
636     dvd_input_saved_delete (this);
637     _x_freep (&this->device);
638     _x_freep (&this->mrl);
639     free (this);
640   }
641 }
642 
dvd_plugin_read_block(input_plugin_t * this_gen,fifo_buffer_t * fifo,off_t nlen)643 static buf_element_t *dvd_plugin_read_block (input_plugin_t *this_gen,
644 						fifo_buffer_t *fifo, off_t nlen) {
645   dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen;
646   buf_element_t      *buf;
647   dvdnav_status_t     result;
648   int                 event, len;
649   int                 finished = 0;
650   unsigned char      *block;
651 
652   (void)nlen;
653 
654   if(fifo == NULL) {
655     xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
656 	    _("input_dvd: values of \\beta will give rise to dom!\n"));
657     return NULL;
658   }
659 
660   if (this->dvdnav) {
661     dvd_input_class_t *class = (dvd_input_class_t *)this->input_plugin.input_class;
662     if (this->user_conf_version < class->user_conf_version) {
663       this->user_conf_version = class->user_conf_version;
664       update_cfg (this);
665     }
666   }
667 
668   /* Read buffer */
669   buf = fifo->buffer_pool_alloc (fifo);
670   block = buf->mem;
671 
672   while(!finished) {
673     dvd_handle_events(this);
674 
675     if (block != buf->mem) {
676       /* if we already have a dvdnav cache block, give it back first */
677       dvdnav_free_cache_block(this->dvdnav, block);
678       block = buf->mem;
679     }
680     result = dvdnav_get_next_cache_block (this->dvdnav, &block, &event, &len);
681     if(result == DVDNAV_STATUS_ERR) {
682       xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
683 	      _("input_dvd: Error getting next block from DVD (%s)\n"), dvdnav_err_to_string(this->dvdnav));
684       _x_message(this->stream, XINE_MSG_READ_ERROR,
685                    dvdnav_err_to_string(this->dvdnav), NULL);
686       if (block != buf->mem) dvdnav_free_cache_block(this->dvdnav, block);
687       buf->free_buffer(buf);
688       return NULL;
689     }
690 
691     switch(event) {
692     case DVDNAV_BLOCK_OK:
693       {
694 	buf->content = block;
695 	buf->type = BUF_DEMUX_BLOCK;
696 
697 	/* Make sure we don't think we are still paused */
698 	this->pause_timer = 0;
699 
700 	/* we got a block, so we might be seekable here */
701 	this->seekable = 1;
702 
703 	finished = 1;
704       }
705       break;
706     case DVDNAV_NOP:
707       break;
708     case DVDNAV_STILL_FRAME:
709       {
710         dvdnav_still_event_t *still_event =
711           (dvdnav_still_event_t*)block;
712         buf->type = BUF_CONTROL_NOP;
713         finished = 1;
714 
715         /* stills are not seekable */
716         this->seekable = 0;
717 
718         /* Xine's method of doing still-frames */
719         if (this->pause_timer == 0) {
720 #ifdef INPUT_DEBUG
721           printf("input_dvd: Stillframe! (pause time = 0x%02x)\n",
722                  still_event->length);
723 #endif
724           this->pause_timer = still_event->length;
725           this->pause_end_time = time(NULL) + this->pause_timer;
726           this->pause_counter = 0;
727           break;
728         }
729 
730         if(this->pause_timer == 0xff) {
731           this->pause_counter++;
732           xine_usec_sleep(50000);
733           break;
734         }
735         if ((this->pause_timer != 0xff) &&
736             (time(NULL) >= this->pause_end_time)) {
737           this->pause_timer = 0;
738           this->pause_end_time = 0;
739           dvdnav_still_skip(this->dvdnav);
740           break;
741         }
742         if(this->pause_timer) {
743           this->pause_counter++;
744 #ifdef INPUT_DEBUG
745           printf("input_dvd: Stillframe! (pause_timer = 0x%02x) counter=%d\n",
746                  still_event->length, this->pause_counter);
747 #endif
748           xine_usec_sleep(50000);
749           break;
750         }
751       }
752       break;
753     case DVDNAV_SPU_STREAM_CHANGE:
754       {
755 	dvdnav_spu_stream_change_event_t *stream_event =
756 	  (dvdnav_spu_stream_change_event_t*) (block);
757         buf->content = block;
758         buf->type = BUF_CONTROL_SPU_CHANNEL;
759         buf->decoder_info[0] = stream_event->physical_wide;
760 	buf->decoder_info[1] = stream_event->physical_letterbox;
761 	buf->decoder_info[2] = stream_event->physical_pan_scan;
762 #ifdef INPUT_DEBUG
763 	printf("input_dvd: SPU stream wide %d, letterbox %d, pan&scan %d\n",
764 	  stream_event->physical_wide,
765 	  stream_event->physical_letterbox,
766 	  stream_event->physical_pan_scan);
767 #endif
768 	finished = 1;
769       }
770       break;
771     case DVDNAV_AUDIO_STREAM_CHANGE:
772       {
773 	dvdnav_audio_stream_change_event_t *stream_event =
774 	 (dvdnav_audio_stream_change_event_t*) (block);
775         buf->content = block;
776         buf->type = BUF_CONTROL_AUDIO_CHANNEL;
777         buf->decoder_info[0] = stream_event->physical;
778 #ifdef INPUT_DEBUG
779 	printf("input_dvd: AUDIO stream %d\n", stream_event->physical);
780 #endif
781 	finished = 1;
782       }
783       break;
784     case DVDNAV_HIGHLIGHT:
785       xine_dvd_send_button_update(this, 0);
786       break;
787     case DVDNAV_VTS_CHANGE:
788       {
789 	int aspect, permission;
790 #ifdef INPUT_DEBUG
791 	printf("input_dvd: VTS change\n");
792 #endif
793 	/* Check for video aspect change and scaling permissions */
794 	aspect = dvdnav_get_video_aspect(this->dvdnav);
795 	permission = dvdnav_get_video_scale_permission(this->dvdnav);
796 
797 	buf->type = BUF_VIDEO_MPEG;
798 	buf->decoder_flags = BUF_FLAG_SPECIAL;
799 	buf->decoder_info[1] = BUF_SPECIAL_ASPECT;
800 	buf->decoder_info[2] = aspect;
801 	buf->decoder_info[3] = permission;
802 	finished = 1;
803       }
804       break;
805     case DVDNAV_CELL_CHANGE:
806       {
807 	dvdnav_cell_change_event_t *cell_event =
808 	 (dvdnav_cell_change_event_t*) (block);
809 
810 	/* Tell xine to update the UI */
811         xine_event_t event = {
812 	  .type = XINE_EVENT_UI_CHANNELS_CHANGED,
813 	  .stream = this->stream,
814 	  .data = NULL,
815 	  .data_length = 0
816 	};
817 	xine_event_send(this->stream, &event);
818 
819 	if( !update_title_display(this) ) {
820 	  if (buf->mem != block) dvdnav_free_cache_block(this->dvdnav, block);
821 	  buf->free_buffer(buf);
822 	  /* return NULL to indicate end of stream */
823 	  return NULL;
824         }
825 
826 	this->pg_length  = cell_event->pg_length;
827 	this->pgc_length = cell_event->pgc_length;
828 	this->cell_start = cell_event->cell_start;
829 	this->pg_start   = cell_event->pg_start;
830       }
831       break;
832     case DVDNAV_HOP_CHANNEL:
833       _x_demux_flush_engine(this->stream);
834       break;
835     case DVDNAV_NAV_PACKET:
836       {
837 	buf->content = block;
838 	buf->type = BUF_DEMUX_BLOCK;
839 	finished = 1;
840       }
841       break;
842     case DVDNAV_SPU_CLUT_CHANGE:
843       {
844 	buf->content = block;
845 	buf->type = BUF_SPU_DVD;
846 	buf->decoder_flags |= BUF_FLAG_SPECIAL;
847 	buf->decoder_info[1] = BUF_SPECIAL_SPU_DVD_SUBTYPE;
848 	buf->decoder_info[2] = SPU_DVD_SUBTYPE_CLUT;
849 	finished = 1;
850       }
851       break;
852     case DVDNAV_STOP:
853       {
854 	if (buf->mem != block) dvdnav_free_cache_block(this->dvdnav, block);
855 	buf->free_buffer(buf);
856 	/* return NULL to indicate end of stream */
857 	return NULL;
858       }
859     case DVDNAV_WAIT:
860       {
861 	int buffers = this->stream->video_fifo->size(this->stream->video_fifo);
862 	if (this->stream->audio_fifo)
863 	  buffers += this->stream->audio_fifo->size(this->stream->audio_fifo);
864 	/* we wait until the fifos are empty, ... well, we allow one remaining buffer,
865 	 * because a flush might be in progress. */
866 	if (buffers <= 1)
867 	  dvdnav_wait_skip(this->dvdnav);
868 	else
869 	  xine_usec_sleep(50000);
870       }
871       break;
872     default:
873       xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "input_dvd: FIXME: Unknown event (%i)\n", event);
874       break;
875     }
876   }
877 
878   if (block != buf->mem) {
879     /* we have received a buffer from the libdvdnav cache, store all
880      * necessary values to reconstruct xine's buffer and modify it according to
881      * our needs. */
882     dvd_input_saved_buf_t *s;
883     pthread_mutex_lock (&this->buf_mutex);
884     s = dvd_input_saved_acquire (this);
885     if (s) {
886       s->used.block = block;
887       s->used.free_buffer = buf->free_buffer;
888       s->used.source = buf->source;
889       buf->free_buffer = dvd_plugin_free_buffer;
890       buf->source = s;
891     } else {
892       /* the stack for storing the memory chunks from xine is full, we cannot
893        * modify the buffer, because we would not be able to reconstruct it.
894        * Therefore we copy the data and give the buffer back. */
895       memcpy(buf->mem, block, DVD_BLOCK_SIZE);
896       dvdnav_free_cache_block(this->dvdnav, block);
897       buf->content = buf->mem;
898     }
899     pthread_mutex_unlock(&this->buf_mutex);
900   }
901 
902   if (this->pg_length && this->pgc_length) {
903     switch (this->user_seek_mode) {
904     case 0: /* PGC based seeking */
905       buf->extra_info->total_time = this->pgc_length / 90;
906       buf->extra_info->input_time = this->cell_start / 90;
907       break;
908     case 1: /* PG based seeking */
909       buf->extra_info->total_time = this->pg_length  / 90;
910       buf->extra_info->input_time = (this->cell_start - this->pg_start) / 90;
911       break;
912     }
913   }
914 
915   return buf;
916 }
917 
dvd_plugin_read(input_plugin_t * this_gen,void * buf_gen,off_t len)918 static off_t dvd_plugin_read (input_plugin_t *this_gen, void *buf_gen, off_t len) {
919 /*  dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen; */
920   uint8_t *buf = buf_gen;
921 
922   (void)this_gen;
923   if (len < 4)
924     return -1;
925 
926   /* FIXME: Tricking the demux_mpeg_block plugin */
927   buf[0] = 0;
928   buf[1] = 0;
929   buf[2] = 0x01;
930   buf[3] = 0xba;
931   return 1;
932 }
933 
dvd_plugin_get_current_pos(input_plugin_t * this_gen)934 static off_t dvd_plugin_get_current_pos (input_plugin_t *this_gen){
935   dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen;
936   uint32_t pos=0;
937   uint32_t length=1;
938   /*dvdnav_status_t result;*/
939   lprintf("Called\n");
940 
941   if (!this->dvdnav) {
942     return 0;
943   }
944   /*result =*/ dvdnav_get_position(this->dvdnav, &pos, &length);
945   return (off_t)pos * (off_t)DVD_BLOCK_SIZE;
946 }
947 
dvd_plugin_seek(input_plugin_t * this_gen,off_t offset,int origin)948 static off_t dvd_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) {
949   dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen;
950 
951   lprintf("Called\n");
952 
953   if (!this->dvdnav) {
954     return -1;
955   }
956 
957   dvdnav_sector_search(this->dvdnav, offset / DVD_BLOCK_SIZE , origin);
958   return dvd_plugin_get_current_pos(this_gen);
959 }
960 
dvd_plugin_seek_time(input_plugin_t * this_gen,int time_offset,int origin)961 static off_t dvd_plugin_seek_time (input_plugin_t *this_gen, int time_offset, int origin) {
962   dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen;
963 
964   lprintf("Called\n");
965 
966   if (!this->dvdnav || origin != SEEK_SET) {
967     return -1;
968   }
969 
970   dvdnav_time_search(this->dvdnav, time_offset * 90);
971 
972   return dvd_plugin_get_current_pos(this_gen);
973 }
974 
dvd_plugin_get_length(input_plugin_t * this_gen)975 static off_t dvd_plugin_get_length (input_plugin_t *this_gen) {
976   dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen;
977   uint32_t pos=0;
978   uint32_t length=1;
979   /*dvdnav_status_t result;*/
980 
981   lprintf("Called\n");
982 
983   if (!this->dvdnav) {
984     return 0;
985   }
986   /*result =*/ dvdnav_get_position(this->dvdnav, &pos, &length);
987   return (off_t)length * (off_t)DVD_BLOCK_SIZE;
988 }
989 
dvd_plugin_get_blocksize(input_plugin_t * this_gen)990 static uint32_t dvd_plugin_get_blocksize (input_plugin_t *this_gen) {
991   lprintf("Called\n");
992 
993   (void)this_gen;
994   return DVD_BLOCK_SIZE;
995 }
996 
dvd_plugin_get_mrl(input_plugin_t * this_gen)997 static const char* dvd_plugin_get_mrl (input_plugin_t *this_gen) {
998   dvd_input_plugin_t *this = (dvd_input_plugin_t*)this_gen;
999 
1000   lprintf("Called\n");
1001 
1002   return this->mrl;
1003 }
1004 
xine_dvd_send_button_update(dvd_input_plugin_t * this,int mode)1005 static void xine_dvd_send_button_update(dvd_input_plugin_t *this, int mode) {
1006   int32_t button;
1007   int32_t show;
1008 
1009   if (_x_stream_info_get(this->stream, XINE_STREAM_INFO_IGNORE_SPU))
1010     return;
1011 
1012   if (!this->stream->spu_decoder_plugin ||
1013       this->stream->spu_decoder_streamtype != ((BUF_SPU_DVD >> 16) & 0xFF)) {
1014     /* the proper SPU decoder has not been initialized yet,
1015      * so we send a dummy buffer to trigger this */
1016     buf_element_t *buf = this->stream->video_fifo->buffer_pool_alloc(this->stream->video_fifo);
1017 
1018     buf->size = 0;
1019     buf->type = BUF_SPU_DVD;
1020     this->stream->video_fifo->insert(this->stream->video_fifo, buf);
1021 
1022     while (!this->stream->spu_decoder_plugin ||
1023 	this->stream->spu_decoder_streamtype != ((BUF_SPU_DVD >> 16) & 0xFF))
1024       xine_usec_sleep(50000);
1025   }
1026 
1027   dvdnav_get_current_highlight(this->dvdnav, &button);
1028 
1029   if (button == this->buttonN && (mode == 0) ) return;
1030 
1031   this->buttonN = button; /* Avoid duplicate sending of button info */
1032 
1033 #ifdef INPUT_DEBUG
1034   printf("input_dvd: sending_button_update button=%d mode=%d\n", button, mode);
1035 #endif
1036   /* Do we want to show or hide the button? */
1037   /* libspudec will control hiding */
1038   show = mode + 1; /* mode=0 select, 1 activate. */
1039   this->stream->spu_decoder_plugin->set_button (this->stream->spu_decoder_plugin, button, show);
1040 }
1041 
dvd_handle_events(dvd_input_plugin_t * this)1042 static void dvd_handle_events(dvd_input_plugin_t *this) {
1043 
1044   dvd_input_class_t  *class = (dvd_input_class_t*)this->input_plugin.input_class;
1045   xine_event_t *event;
1046 
1047   while ((event = xine_event_get(this->event_queue))) {
1048 
1049     if(!this->dvdnav) {
1050       xine_event_free(event);
1051       return;
1052     }
1053 
1054     switch(event->type) {
1055     case XINE_EVENT_INPUT_MENU1:
1056       xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "input_dvd: MENU1 key hit.\n");
1057       dvdnav_menu_call(this->dvdnav, DVD_MENU_Escape);
1058       break;
1059     case XINE_EVENT_INPUT_MENU2:
1060       xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "input_dvd: MENU2 key hit.\n");
1061       dvdnav_menu_call(this->dvdnav, DVD_MENU_Title);
1062       break;
1063     case XINE_EVENT_INPUT_MENU3:
1064       xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "input_dvd: MENU3 key hit.\n");
1065       if (dvdnav_menu_call(this->dvdnav, DVD_MENU_Root) == DVDNAV_STATUS_ERR)
1066         dvdnav_menu_call(this->dvdnav, DVD_MENU_Title);
1067       break;
1068     case XINE_EVENT_INPUT_MENU4:
1069       xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "input_dvd: MENU4 key hit.\n");
1070       dvdnav_menu_call(this->dvdnav, DVD_MENU_Subpicture);
1071       break;
1072     case XINE_EVENT_INPUT_MENU5:
1073       xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "input_dvd: MENU5 key hit.\n");
1074       dvdnav_menu_call(this->dvdnav, DVD_MENU_Audio);
1075       break;
1076     case XINE_EVENT_INPUT_MENU6:
1077       xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "input_dvd: MENU6 key hit.\n");
1078       dvdnav_menu_call(this->dvdnav, DVD_MENU_Angle);
1079       break;
1080     case XINE_EVENT_INPUT_MENU7:
1081       xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "input_dvd: MENU7 key hit.\n");
1082       dvdnav_menu_call(this->dvdnav, DVD_MENU_Part);
1083       break;
1084     case XINE_EVENT_INPUT_NEXT:
1085       {
1086 	int title = 0, part = 0;
1087         switch (class->user_skip_mode) {
1088 	case 0: /* skip by program */
1089 	  dvdnav_next_pg_search(this->dvdnav);
1090 	  break;
1091 	case 1: /* skip by part */
1092 	  if (dvdnav_current_title_info(this->dvdnav, &title, &part) && title > 0)
1093 	    dvdnav_part_play(this->dvdnav, title, ++part);
1094 	  break;
1095 	case 2: /* skip by title */
1096 	  if (dvdnav_current_title_info(this->dvdnav, &title, &part) && title > 0)
1097 	    dvdnav_part_play(this->dvdnav, ++title, 1);
1098 	  break;
1099 	}
1100       }
1101       break;
1102     case XINE_EVENT_INPUT_PREVIOUS:
1103       {
1104 	int title = 0, part = 0;
1105         switch (class->user_skip_mode) {
1106 	case 0: /* skip by program */
1107 	  dvdnav_prev_pg_search(this->dvdnav);
1108 	  break;
1109 	case 1: /* skip by part */
1110 	  if (dvdnav_current_title_info(this->dvdnav, &title, &part) && title > 0)
1111 	    dvdnav_part_play(this->dvdnav, title, --part);
1112 	  break;
1113 	case 2: /* skip by title */
1114 	  if (dvdnav_current_title_info(this->dvdnav, &title, &part) && title > 0)
1115 	    dvdnav_part_play(this->dvdnav, --title, 1);
1116 	  break;
1117 	}
1118       }
1119       break;
1120     case XINE_EVENT_INPUT_ANGLE_NEXT:
1121       {
1122         int num = 0, current = 0;
1123         dvdnav_get_angle_info(this->dvdnav, &current, &num);
1124 
1125         if(num != 0) {
1126           current ++;
1127           if(current > num)
1128             current = 1;
1129         }
1130         dvdnav_angle_change(this->dvdnav, current);
1131 #ifdef INPUT_DEBUG
1132         printf("input_dvd: Changing to angle %i\n", current);
1133 #endif
1134         update_title_display(this);
1135       }
1136       break;
1137     case XINE_EVENT_INPUT_ANGLE_PREVIOUS:
1138       {
1139         int num = 0, current = 0;
1140         dvdnav_get_angle_info(this->dvdnav, &current, &num);
1141 
1142         if(num != 0) {
1143           current --;
1144           if(current <= 0)
1145             current = num;
1146         }
1147         dvdnav_angle_change(this->dvdnav, current);
1148 #ifdef INPUT_DEBUG
1149         printf("input_dvd: Changing to angle %i\n", current);
1150 #endif
1151         update_title_display(this);
1152       }
1153       break;
1154     case XINE_EVENT_INPUT_SELECT:
1155       {
1156         pci_t nav_pci;
1157         if (!this->stream->spu_decoder_plugin) {
1158           return;
1159         }
1160         if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) {
1161           if (dvdnav_button_activate(this->dvdnav, &nav_pci) == DVDNAV_STATUS_OK)
1162             xine_dvd_send_button_update(this, 1);
1163         }
1164       }
1165       break;
1166     case XINE_EVENT_INPUT_MOUSE_BUTTON:
1167       {
1168         pci_t nav_pci;
1169         if (!this->stream->spu_decoder_plugin) {
1170           return;
1171         }
1172         if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) {
1173 	  xine_input_data_t *input = event->data;
1174           if((input->button == 1) && dvdnav_mouse_activate(this->dvdnav,
1175 							   &nav_pci, input->x, input->y) == DVDNAV_STATUS_OK) {
1176             xine_dvd_send_button_update(this, 1);
1177 
1178 	    if(this->mouse_in)
1179 	      send_mouse_enter_leave_event(this, 0);
1180 
1181 	    this->mouse_buttonN = -1;
1182 
1183 	  }
1184         }
1185       }
1186       break;
1187     case XINE_EVENT_INPUT_BUTTON_FORCE:  /* For libspudec to feedback forced button select from NAV PCI packets. */
1188       {
1189         pci_t nav_pci;
1190         int *but = event->data;
1191 #ifdef INPUT_DEBUG
1192         printf("input_dvd: BUTTON_FORCE %d\n", *but);
1193 #endif
1194         if (!this->stream->spu_decoder_plugin)
1195           return;
1196         if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) )
1197           dvdnav_button_select(this->dvdnav, &nav_pci, *but);
1198       }
1199       break;
1200     case XINE_EVENT_INPUT_MOUSE_MOVE:
1201       {
1202         pci_t nav_pci;
1203         if (!this->stream->spu_decoder_plugin)
1204           return;
1205         if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) {
1206 	  xine_input_data_t *input = event->data;
1207 	  /* printf("input_dvd: Mouse move (x,y) = (%i,%i)\n", input->x, input->y); */
1208 	  if(dvdnav_mouse_select(this->dvdnav, &nav_pci, input->x, input->y) == DVDNAV_STATUS_OK) {
1209 	    int32_t button;
1210 
1211 	    dvdnav_get_current_highlight(this->dvdnav, &button);
1212 
1213 	    if(this->mouse_buttonN != button) {
1214 	      this->mouse_buttonN = button;
1215 	      send_mouse_enter_leave_event(this, 1);
1216 	    }
1217 
1218 	  }
1219 	  else {
1220 	    if(this->mouse_in)
1221 	      send_mouse_enter_leave_event(this, 0);
1222 
1223 	  }
1224         }
1225       }
1226       break;
1227     case XINE_EVENT_INPUT_UP:
1228       {
1229         pci_t nav_pci;
1230         if (!this->stream->spu_decoder_plugin)
1231           return;
1232         if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) {
1233           dvdnav_upper_button_select(this->dvdnav, &nav_pci);
1234 
1235 	  if(this->mouse_in)
1236 	    send_mouse_enter_leave_event(this, 0);
1237 
1238 	}
1239         break;
1240       }
1241     case XINE_EVENT_INPUT_DOWN:
1242       {
1243         pci_t nav_pci;
1244         if (!this->stream->spu_decoder_plugin)
1245           return;
1246         if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) {
1247           dvdnav_lower_button_select(this->dvdnav, &nav_pci);
1248 
1249 	  if(this->mouse_in)
1250 	    send_mouse_enter_leave_event(this, 0);
1251 
1252 	}
1253         break;
1254       }
1255     case XINE_EVENT_INPUT_LEFT:
1256       {
1257         pci_t nav_pci;
1258         if (!this->stream->spu_decoder_plugin)
1259           return;
1260         if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) {
1261           dvdnav_left_button_select(this->dvdnav, &nav_pci);
1262 
1263 	  if(this->mouse_in)
1264 	    send_mouse_enter_leave_event(this, 0);
1265 
1266 	}
1267         break;
1268       }
1269     case XINE_EVENT_INPUT_RIGHT:
1270       {
1271         pci_t nav_pci;
1272         if (!this->stream->spu_decoder_plugin)
1273           return;
1274         if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) {
1275           dvdnav_right_button_select(this->dvdnav, &nav_pci);
1276 
1277 	  if(this->mouse_in)
1278 	    send_mouse_enter_leave_event(this, 0);
1279 
1280 	}
1281         break;
1282       }
1283     case XINE_EVENT_INPUT_NUMBER_9:
1284       this->typed_buttonN++;
1285       /* fall through */
1286     case XINE_EVENT_INPUT_NUMBER_8:
1287       this->typed_buttonN++;
1288       /* fall through */
1289     case XINE_EVENT_INPUT_NUMBER_7:
1290       this->typed_buttonN++;
1291       /* fall through */
1292     case XINE_EVENT_INPUT_NUMBER_6:
1293       this->typed_buttonN++;
1294       /* fall through */
1295     case XINE_EVENT_INPUT_NUMBER_5:
1296       this->typed_buttonN++;
1297       /* fall through */
1298     case XINE_EVENT_INPUT_NUMBER_4:
1299       this->typed_buttonN++;
1300       /* fall through */
1301     case XINE_EVENT_INPUT_NUMBER_3:
1302       this->typed_buttonN++;
1303       /* fall through */
1304     case XINE_EVENT_INPUT_NUMBER_2:
1305       this->typed_buttonN++;
1306       /* fall through */
1307     case XINE_EVENT_INPUT_NUMBER_1:
1308       this->typed_buttonN++;
1309       /* fall through */
1310     case XINE_EVENT_INPUT_NUMBER_0:
1311       {
1312         pci_t nav_pci;
1313         if (!this->stream->spu_decoder_plugin)
1314           return;
1315         if (this->stream->spu_decoder_plugin->get_interact_info(this->stream->spu_decoder_plugin, &nav_pci) ) {
1316 	  if (dvdnav_button_select_and_activate(this->dvdnav, &nav_pci, this->typed_buttonN) == DVDNAV_STATUS_OK) {
1317             xine_dvd_send_button_update(this, 1);
1318 
1319 	    if(this->mouse_in)
1320 	      send_mouse_enter_leave_event(this, 0);
1321 	  }
1322 
1323           this->typed_buttonN = 0;
1324         }
1325         break;
1326       }
1327     case XINE_EVENT_INPUT_NUMBER_10_ADD:
1328       this->typed_buttonN += 10;
1329     }
1330 
1331     xine_event_free(event);
1332   }
1333   return;
1334 }
1335 
dvd_plugin_get_optional_data(input_plugin_t * this_gen,void * data,int data_type)1336 static int dvd_plugin_get_optional_data (input_plugin_t *this_gen,
1337 					    void *data, int data_type) {
1338   dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
1339 
1340   switch(data_type) {
1341 
1342   case INPUT_OPTIONAL_DATA_AUDIOLANG: {
1343     uint16_t lang;
1344     int      channel;
1345     int8_t   dvd_channel;
1346 
1347     memcpy(&channel, data, sizeof(channel));
1348 
1349     /* Be paranoid */
1350     if(this && this->stream && this->dvdnav) {
1351 
1352       if(!(dvdnav_is_domain_vts(this->dvdnav))) {
1353 	strcpy(data, "menu");
1354 	if (channel <= 0)
1355 	  return INPUT_OPTIONAL_SUCCESS;
1356 	else
1357 	  return INPUT_OPTIONAL_UNSUPPORTED;
1358       }
1359 
1360       if (channel == -1)
1361         dvd_channel = dvdnav_get_audio_logical_stream(this->dvdnav, this->stream->audio_channel_auto);
1362       else
1363         dvd_channel = dvdnav_get_audio_logical_stream(this->dvdnav, channel);
1364 
1365       if(dvd_channel != -1) {
1366 	lang = dvdnav_audio_stream_to_lang(this->dvdnav, dvd_channel);
1367 
1368 	if(lang != 0xffff)
1369 	  sprintf(data, " %c%c", lang >> 8, lang & 0xff);
1370 	  /* TODO: provide long version in XINE_META_INFO_FULL_LANG */
1371 	else
1372 	  strcpy(data, " ??");
1373 	return INPUT_OPTIONAL_SUCCESS;
1374       } else {
1375         if (channel == -1) {
1376 	  strcpy(data, "none");
1377 	  return INPUT_OPTIONAL_SUCCESS;
1378 	}
1379       }
1380     }
1381     return INPUT_OPTIONAL_UNSUPPORTED;
1382   }
1383   break;
1384 
1385 
1386   case INPUT_OPTIONAL_DATA_SPULANG: {
1387     uint16_t lang;
1388     int      channel;
1389     int8_t   dvd_channel;
1390 
1391     memcpy(&channel, data, sizeof(channel));
1392 
1393     /* Be paranoid */
1394     if(this && this->stream && this->dvdnav) {
1395 
1396       if(!(dvdnav_is_domain_vts(this->dvdnav))) {
1397 	strcpy(data, "menu");
1398 	if (channel <= 0)
1399 	  return INPUT_OPTIONAL_SUCCESS;
1400 	else
1401 	  return INPUT_OPTIONAL_UNSUPPORTED;
1402       }
1403 
1404       if(channel == -1)
1405 	dvd_channel = dvdnav_get_spu_logical_stream(this->dvdnav, this->stream->spu_channel_auto);
1406       else
1407 	dvd_channel = dvdnav_get_spu_logical_stream(this->dvdnav, channel);
1408 
1409       if(dvd_channel != -1) {
1410 	lang = dvdnav_spu_stream_to_lang(this->dvdnav, dvd_channel);
1411 
1412 	if(lang != 0xffff)
1413 	  sprintf(data, " %c%c", lang >> 8, lang & 0xff);
1414 	  /* TODO: provide long version in XINE_META_INFO_FULL_LANG */
1415 	else
1416 	  sprintf(data, " %c%c", '?', '?');
1417 	return INPUT_OPTIONAL_SUCCESS;
1418       } else {
1419 	if(channel == -1) {
1420 	  strcpy(data, "none");
1421 	  return INPUT_OPTIONAL_SUCCESS;
1422 	}
1423       }
1424     }
1425     return INPUT_OPTIONAL_UNSUPPORTED;
1426   }
1427   break;
1428 
1429   }
1430 
1431   return INPUT_OPTIONAL_UNSUPPORTED;
1432 }
1433 
1434 #ifdef	__sun
1435 /*
1436  * Check the environment, if we're running under sun's
1437  * vold/rmmount control.
1438  */
1439 static void
check_solaris_vold_device(dvd_input_class_t * this)1440 check_solaris_vold_device(dvd_input_class_t *this)
1441 {
1442   char *volume_device;
1443   char *volume_name;
1444   char *volume_action;
1445   char *device;
1446   struct stat stb;
1447 
1448   if ((volume_device = getenv("VOLUME_DEVICE")) != NULL &&
1449       (volume_name   = getenv("VOLUME_NAME"))   != NULL &&
1450       (volume_action = getenv("VOLUME_ACTION")) != NULL &&
1451       strcmp(volume_action, "insert") == 0) {
1452 
1453     device = _x_asprintf("%s/%s", volume_device, volume_name);
1454     if (!device || stat(device, &stb) != 0 || !S_ISCHR(stb.st_mode)) {
1455       free(device);
1456       return;
1457     }
1458     this->dvd_device = device;
1459   }
1460 }
1461 #endif
1462 
1463 
dvd_parse_try_open(dvd_input_plugin_t * this,const char * locator)1464 static int dvd_parse_try_open(dvd_input_plugin_t *this, const char *locator)
1465 {
1466   const char *intended_dvd_device;
1467 
1468   /* FIXME: we temporarily special-case "dvd:/" for compatibility;
1469    * actually "dvd:/" should play a DVD image stored in /, but for
1470    * now we have it use the default device */
1471 #if 0
1472   if (strlen(locator))
1473 #else
1474   if (strlen(locator) && !(locator[0] == '/' && locator[1] == '\0'))
1475 #endif
1476   {
1477     /* we have an alternative dvd_path */
1478     intended_dvd_device = locator;
1479     /* do not use the raw device for the alternative */
1480     /*xine_setenv("DVDCSS_RAW_DEVICE", "", 1);*/
1481   } else {
1482     /* use default DVD device */
1483     dvd_input_class_t *class = (dvd_input_class_t*)this->input_plugin.input_class;
1484     /*
1485     xine_cfg_entry_t raw_device;
1486     if (xine_config_lookup_entry(this->stream->xine,
1487 	"media.dvd.raw_device", &raw_device))
1488       xine_setenv("DVDCSS_RAW_DEVICE", raw_device.str_value, 1);
1489     */
1490     intended_dvd_device = class->dvd_device;
1491   }
1492 
1493   /* attempt to open DVD */
1494   if (this->opened) {
1495     if (this->device && !strcmp (intended_dvd_device, this->device)) {
1496       /* Already open, so skip opening */
1497       dvdnav_reset(this->dvdnav);
1498     } else {
1499       /* Changing DVD device */
1500       dvdnav_close(this->dvdnav);
1501       this->dvdnav = NULL;
1502       this->opened = 0;
1503       _x_freep (&this->device);
1504     }
1505   }
1506   if (!this->opened) {
1507     if (dvdnav_open(&this->dvdnav, intended_dvd_device) == DVDNAV_STATUS_OK) {
1508       this->opened = 1;
1509       this->device = strdup (intended_dvd_device);
1510     }
1511   }
1512 
1513   return this->opened;
1514 }
1515 
dvd_parse_mrl(dvd_input_plugin_t * this)1516 static int dvd_parse_mrl (dvd_input_plugin_t *this) {
1517   /* we already checked the "dvd:/" MRL before */
1518   size_t mlen = strlen (this->mrl + 4);
1519   char *b = malloc (mlen + 5);
1520 
1521   if (!b)
1522     return MODE_FAIL;
1523   memset (b, 0, 4);
1524   memcpy (b + 4, this->mrl + 4, mlen);
1525   b[4 + mlen] = 0;
1526 
1527   this->title   = -1;
1528   this->part    = -1;
1529 
1530   do {
1531     uint8_t *p;
1532 
1533     _x_mrl_unescape (b + 4);
1534 
1535     if (dvd_parse_try_open (this, b + 4)) {
1536       free (b);
1537       return MODE_NAVIGATE;
1538     }
1539 
1540     /* opening failed, but we can still try cutting off <title>.<part> */
1541     mlen = strlen (b + 4);
1542     p = (uint8_t *)b + 4 + mlen - 1;
1543 
1544     {
1545       uint32_t v = 0, f = 1;
1546       while (1) {
1547         uint32_t z = *p ^ '0';
1548         if (z > 9)
1549           break;
1550         v += f * z;
1551         f *= 10u;
1552         p--;
1553       }
1554       this->title = v;
1555     }
1556 
1557     if (*p == '.') {
1558       uint32_t v = 0, f = 1;
1559       this->part = this->title;
1560       p--;
1561       while (1) {
1562         uint32_t z = *p ^ '0';
1563         if (z > 9)
1564           break;
1565         v += f * z;
1566         f *= 10u;
1567         p--;
1568       }
1569       this->title = v;
1570     }
1571 
1572     if (p == (uint8_t *)b + 4 + mlen - 1)
1573       break;
1574 
1575     /* never delete the very first slash, since this will turn an
1576      * absolute into a relative URL and overthrow further opening */
1577     if ((*p != '/') || (p <= (uint8_t *)b + 4))
1578       p++;
1579     *p = 0;
1580 
1581     if (dvd_parse_try_open (this, b + 4)) {
1582       free (b);
1583       return this->title >= 0 ? MODE_TITLE : MODE_NAVIGATE;
1584     }
1585   } while (0);
1586 
1587   free (b);
1588   return MODE_FAIL;
1589 }
1590 
dvd_plugin_open(input_plugin_t * this_gen)1591 static int dvd_plugin_open (input_plugin_t *this_gen) {
1592   dvd_input_plugin_t    *this = (dvd_input_plugin_t*)this_gen;
1593   dvd_input_class_t     *class = (dvd_input_class_t*)this_gen->input_class;
1594 
1595   lprintf("Called\n");
1596 
1597   this->mode = dvd_parse_mrl (this);
1598 
1599   if (this->mode == MODE_FAIL) {
1600     /* opening failed and we have nothing left to try */
1601     xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("input_dvd: Error opening DVD device\n"));
1602     _x_message(this->stream, XINE_MSG_READ_ERROR,
1603       /* FIXME: see FIXME in dvd_parse_try_open() */
1604       (this->mrl[0] && !(this->mrl[0] == '/' && this->mrl[1] == '\0')) ? this->mrl : class->dvd_device, NULL);
1605     return 0;
1606   }
1607 
1608   dvdnav_get_title_string(this->dvdnav, &this->dvd_name);
1609   if(this->dvd_name)
1610     _x_meta_info_set(this->stream, XINE_META_INFO_TITLE, this->dvd_name);
1611 
1612   apply_cfg (this);
1613 
1614   if (this->mode == MODE_TITLE) {
1615     int titles, parts;
1616 
1617     /* a <title>.<part> was specified -> resume parsing */
1618 
1619     dvdnav_get_number_of_titles(this->dvdnav, &titles);
1620     if (this->title > titles) {
1621       xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
1622 	      "input_dvd: Title %i is out of range (1 to %i).\n", this->title, titles);
1623       dvdnav_close(this->dvdnav);
1624       this->dvdnav = NULL;
1625       return 0;
1626     }
1627 
1628     /* If there was a part specified, get that too. */
1629     if (this->part >= 0) {
1630       dvdnav_get_number_of_parts (this->dvdnav, this->title, &parts);
1631       if (this->part > parts) {
1632 	xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
1633 		"input_dvd: Part %i is out of range (1 to %i).\n", this->part, parts);
1634 	dvdnav_close(this->dvdnav);
1635 	this->dvdnav = NULL;
1636 	return 0;
1637       }
1638     }
1639 
1640 #ifdef INPUT_DEBUG
1641     printf ("input_dvd: Jumping to TT >%i<, PTT >%i<\n", this->title, this->part);
1642 #endif
1643     if (this->title > 0) {
1644       if (this->part > 0)
1645         dvdnav_part_play (this->dvdnav, this->title, this->part);
1646       else
1647         dvdnav_title_play (this->dvdnav, this->title);
1648     } else
1649       this->mode = MODE_NAVIGATE;
1650   }
1651 #ifdef INPUT_DEBUG
1652   printf("input_dvd: DVD device successfully opened.\n");
1653 #endif
1654 
1655   /* remember the last successfully opened device for ejecting */
1656   free(class->eject_device);
1657   class->eject_device = strdup (this->device);
1658 
1659   { /* Tell Xine to update the UI */
1660     const xine_event_t event = {
1661       .type = XINE_EVENT_UI_CHANNELS_CHANGED,
1662       .stream = this->stream,
1663       .data = NULL,
1664       .data_length = 0
1665     };
1666     xine_event_send(this->stream, &event);
1667   }
1668 
1669   update_title_display(this);
1670 
1671   return 1;
1672 }
1673 
1674 /* dvdnav CLASS functions */
1675 
1676 /*
1677  * Opens the DVD plugin. The MRL takes the following form:
1678  *
1679  * dvd:[dvd_path]/[vts[.program]]
1680  *
1681  * e.g.
1682  *   dvd:/                    - Play (navigate)
1683  *   dvd:/1                   - Play Title 1
1684  *   dvd:/1.3                 - Play Title 1, program 3
1685  *   dvd:/dev/dvd2/           - Play (navigate) from /dev/dvd2
1686  *   dvd:/dev/dvd2/1.3        - Play Title 1, program 3 from /dev/dvd2
1687  */
dvd_class_get_instance(input_class_t * class_gen,xine_stream_t * stream,const char * data)1688 static input_plugin_t *dvd_class_get_instance (input_class_t *class_gen, xine_stream_t *stream, const char *data) {
1689   dvd_input_plugin_t    *this;
1690   static const char handled_mrl[] = "dvd:/";
1691 
1692   lprintf("Called\n");
1693 
1694   /* Check we can handle this MRL */
1695   if (strncasecmp (data, handled_mrl, sizeof(handled_mrl)-1 ) != 0)
1696     return NULL;
1697 
1698   this = calloc(1, sizeof (dvd_input_plugin_t));
1699   if (!this) {
1700     return NULL;
1701   }
1702 #ifndef HAVE_ZERO_SAFE_MEM
1703   this->dvdnav        = NULL;
1704   this->opened        = 0;
1705   this->seekable      = 0;
1706   this->buttonN       = 0;
1707   this->mouse_in      = 0;
1708   this->typed_buttonN = 0;
1709   this->pause_timer   = 0;
1710   this->pg_length     = 0;
1711   this->pgc_length    = 0;
1712   this->dvd_name      = NULL;
1713   this->device        = NULL;
1714   this->freeing       = 0;
1715 #endif
1716   if (dvd_input_saved_new (this)) {
1717     free(this);
1718     return NULL;
1719   }
1720 
1721   this->input_plugin.open               = dvd_plugin_open;
1722   this->input_plugin.get_capabilities   = dvd_plugin_get_capabilities;
1723   this->input_plugin.read               = dvd_plugin_read;
1724   this->input_plugin.read_block         = dvd_plugin_read_block;
1725   this->input_plugin.seek               = dvd_plugin_seek;
1726   this->input_plugin.seek_time          = dvd_plugin_seek_time;
1727   this->input_plugin.get_current_pos    = dvd_plugin_get_current_pos;
1728   this->input_plugin.get_length         = dvd_plugin_get_length;
1729   this->input_plugin.get_blocksize      = dvd_plugin_get_blocksize;
1730   this->input_plugin.get_mrl            = dvd_plugin_get_mrl;
1731   this->input_plugin.get_optional_data  = dvd_plugin_get_optional_data;
1732   this->input_plugin.dispose            = dvd_plugin_dispose;
1733   this->input_plugin.input_class        = class_gen;
1734 
1735   this->user_conf_version = 0;
1736 
1737   this->stream = stream;
1738   _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HAS_STILL, 1);
1739 
1740   this->mouse_buttonN          = -1;
1741   this->mrl                    = strdup(data);
1742 
1743   pthread_mutex_init(&this->buf_mutex, NULL);
1744 
1745   this->event_queue = xine_event_new_queue (this->stream);
1746 
1747   return &this->input_plugin;
1748 }
1749 
1750 /* FIXME: adapt to new api. */
1751 #if 0
1752 static xine_mrl_t **dvd_class_get_dir (input_class_t *this_gen,
1753 						       const char *filename, int *nFiles) {
1754   dvd_input_class_t *this = (dvd_input_class_t*)this_gen;
1755 
1756   lprintf("Called\n");
1757   if (filename) { *nFiles = 0; return NULL; }
1758 
1759 /*
1760   dvd_build_mrl_list(this);
1761   *nFiles = this->num_mrls;
1762   return this->mrls;
1763 */
1764   *nFiles = 0;
1765    return NULL;
1766 }
1767 #endif
1768 
dvd_class_get_autoplay_list(input_class_t * this_gen,int * num_files)1769 static const char * const *dvd_class_get_autoplay_list (input_class_t *this_gen,
1770 					    int *num_files) {
1771 
1772   static const char * const filelist[] = {"dvd:/", NULL};
1773 
1774   lprintf("get_autoplay_list entered\n");
1775 
1776   (void)this_gen;
1777 
1778   *num_files = 1;
1779 
1780   return filelist;
1781 }
1782 
dvd_class_dispose(input_class_t * this_gen)1783 static void dvd_class_dispose(input_class_t *this_gen) {
1784   dvd_input_class_t *this = (dvd_input_class_t*)this_gen;
1785   config_values_t *config = this->xine->config;
1786 
1787   config->unregister_callbacks (config, NULL, NULL, this, sizeof (*this));
1788 
1789   _x_freep(&this->eject_device);
1790   free(this);
1791 }
1792 
dvd_class_eject_media(input_class_t * this_gen)1793 static int dvd_class_eject_media (input_class_t *this_gen) {
1794   dvd_input_class_t *this = (dvd_input_class_t*)this_gen;
1795 
1796   return media_eject_media (this->xine, this->eject_device);
1797 }
1798 
init_class(xine_t * xine,const void * data)1799 static void *init_class (xine_t *xine, const void *data) {
1800   dvd_input_class_t   *this;
1801   config_values_t     *config = xine->config;
1802   void                *dvdcss;
1803   static const char *const skip_modes[] = {"skip program", "skip part", "skip title", NULL};
1804   static const char *const seek_modes[] = {"seek in program chain", "seek in program", NULL};
1805   static const char *const play_single_chapter_modes[] = {"entire dvd", "one chapter", NULL};
1806 
1807   lprintf("Called\n");
1808 #ifdef INPUT_DEBUG
1809   printf("input_dvd.c: init_class called.\n");
1810   printf("input_dvd.c: config = %p\n", config);
1811 #endif
1812 
1813   (void)data;
1814 
1815   this = (dvd_input_class_t *) calloc(1, sizeof (dvd_input_class_t));
1816   if (!this)
1817     return NULL;
1818 #ifndef HAVE_ZERO_SAFE_MEM
1819   this->input_class.get_dir            = NULL;
1820 #endif
1821   this->input_class.get_instance       = dvd_class_get_instance;
1822   this->input_class.identifier         = "DVD";
1823   this->input_class.description        = N_("DVD Navigator");
1824 /*
1825   this->input_class.get_dir            = dvd_class_get_dir;
1826 */
1827   this->input_class.get_autoplay_list  = dvd_class_get_autoplay_list;
1828   this->input_class.dispose            = dvd_class_dispose;
1829   this->input_class.eject_media        = dvd_class_eject_media;
1830 
1831   this->xine                           = xine;
1832 
1833 
1834   this->dvd_device = config->register_filename(config,
1835 					     "media.dvd.device",
1836 					     DVD_PATH, XINE_CONFIG_STRING_IS_DEVICE_NAME,
1837 					     _("device used for DVD playback"),
1838 					     _("The path to the device, usually a "
1839 					       "DVD drive, which you intend to use for playing DVDs."),
1840 					     10, device_change_cb, (void *)this);
1841 
1842 #ifdef HOST_OS_DARWIN
1843   if ((dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY)) != NULL)
1844 #else
1845   if ((dvdcss = dlopen("libdvdcss.so.2", RTLD_LAZY)) != NULL)
1846 #endif
1847   {
1848     /* we have found libdvdcss, enable the specific config options */
1849     /* char *raw_device; */
1850     static const char *const decrypt_modes[] = { "key", "disc", "title", NULL };
1851     int mode;
1852 
1853     /*
1854     raw_device = config->register_filename(config, "media.dvd.raw_device",
1855 					 RDVD_PATH, XINE_CONFIG_STRING_IS_DEVICE_NAME,
1856 					 _("raw device set up for DVD access"),
1857 					 _("If this points to a raw device connected to your "
1858 					   "DVD device, xine will use the raw device for playback. "
1859 					   "This has the advantage of being slightly faster and "
1860 					   "of bypassing the block device cache, which avoids "
1861 					   "throwing away important cache content by keeping DVD "
1862 					   "data cached. Using the block device cache for DVDs "
1863 					   "is useless, because almost all DVD data will be used "
1864 					   "only once.\nSee the documentation on raw device setup "
1865 					   "(man raw) for further information."),
1866 					 10, NULL, NULL);
1867     if (raw_device) xine_setenv("DVDCSS_RAW_DEVICE", raw_device, 0);
1868     */
1869 
1870     mode = config->register_enum(config, "media.dvd.css_decryption_method", 0,
1871 				 (char **)decrypt_modes, _("CSS decryption method"),
1872 				 _("Selects the decryption method libdvdcss will use to descramble "
1873 				   "copy protected DVDs. Try the various methods, if you have problems "
1874 				   "playing scrambled DVDs."), 20, NULL, NULL);
1875     xine_setenv("DVDCSS_METHOD", decrypt_modes[mode], 0);
1876 
1877     if(xine->verbosity > XINE_VERBOSITY_NONE)
1878       xine_setenv("DVDCSS_VERBOSE", "2", 0);
1879     else
1880       xine_setenv("DVDCSS_VERBOSE", "0", 0);
1881 
1882     dlclose(dvdcss);
1883   }
1884 
1885   this->user_conf_version = 1;
1886 
1887   this->user_region = config->register_num (config, "media.dvd.region", 1,
1888     _("region the DVD player claims to be in (1 to 8)"),
1889     _("This only needs to be changed if your DVD jumps to a screen "
1890       "complaining about a wrong region code. It has nothing to do with "
1891       "the region code set in DVD drives, this is purely software."),
1892     0, region_changed_cb, this);
1893   if ((this->user_region < 1) || (this->user_region > 8))
1894     this->user_region = 1;
1895 
1896   {
1897     const char *lang = config->register_string (config, "media.dvd.language", "en",
1898       _("default language for DVD playback"),
1899       _("xine tries to use this language as a default for DVD playback. "
1900         "As far as the DVD supports it, menus and audio tracks will be presented "
1901         "in this language.\nThe value must be a two character ISO639 language code."),
1902       0, language_changed_cb, this);
1903     if (lang)
1904       strlcpy (this->user_lang4, lang, 4);
1905   }
1906 
1907   this->user_read_ahead = config->register_bool (config, "media.dvd.readahead", 1,
1908     _("read-ahead caching"),
1909     _("xine can use a read ahead cache for DVD drive access.\n"
1910       "This may lead to jerky playback on slow drives, but it improves the impact "
1911       "of the DVD layer change on faster drives."),
1912     10, read_ahead_cb, this);
1913 
1914   this->user_skip_mode = config->register_enum (config, "media.dvd.skip_behaviour", 0,
1915     (char **)skip_modes,
1916     _("unit for the skip action"),
1917     _("You can configure the behaviour when issuing a skip command (using the skip "
1918       "buttons for example). The individual values mean:\n\n"
1919       "skip program\n"
1920       "will skip a DVD program, which is a navigational unit similar to the "
1921       "index marks on an audio CD; this is the normal behaviour for DVD players\n\n"
1922       "skip part\n"
1923       "will skip a DVD part, which is a structural unit similar to the "
1924       "track marks on an audio CD; parts usually coincide with programs, but parts "
1925       "can be larger than programs\n\n"
1926       "skip title\n"
1927       "will skip a DVD title, which is a structural unit representing entire "
1928       "features on the DVD"),
1929     20, skip_changed_cb, this);
1930 
1931   this->user_seek_mode = config->register_enum (config, "media.dvd.seek_behaviour", 0,
1932     (char **)seek_modes,
1933     _("unit for seeking"),
1934     _("You can configure the domain spanned by the seek slider. The individual values mean:\n\n"
1935       "seek in program chain\n"
1936       "seeking will span an entire DVD program chain, which is a navigational "
1937       "unit representing the entire video stream of the current feature\n\n"
1938       "seek in program\n"
1939       "seeking will span a DVD program, which is a navigational unit representing "
1940       "a chapter of the current feature"),
1941     20, seek_mode_cb, this);
1942 
1943   this->user_play_single_chapter = config->register_enum (config, "media.dvd.play_single_chapter", 0,
1944     (char **)play_single_chapter_modes,
1945     _("play mode when title/chapter is given"),
1946     _("You can configure the behaviour when playing a dvd from a given "
1947       "title/chapter (eg. using MRL 'dvd:/1.2'). The individual values mean:\n\n"
1948       "entire dvd\n"
1949       "play the entire dvd starting on the specified position.\n\n"
1950       "one chapter\n"
1951       "play just the specified title/chapter and then stop"),
1952     20, play_single_chapter_cb, this);
1953 
1954 #ifdef __sun
1955   check_solaris_vold_device(this);
1956 #endif
1957 #ifdef INPUT_DEBUG
1958   printf("input_dvd.c: init_class finished.\n");
1959 #endif
1960   return this;
1961 }
1962 
1963 
1964 const plugin_info_t xine_plugin_info[] EXPORTED = {
1965   /* type, API, "name", version, special_info, init_function */
1966   { PLUGIN_INPUT | PLUGIN_MUST_PRELOAD, 18, "DVD", XINE_VERSION_CODE, NULL, init_class },
1967   { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
1968 };
1969 
1970