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, ¤t, &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, ¤t, &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