1 /*
2 * Copyright (C) 2000-2020 the xine project
3 * Copyright (C) 2009-2011 Petri Hintukainen <phintuka@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 * Input plugin for BluRay discs / images
22 *
23 * Requires libbluray 0.2.1 or later:
24 * http://www.videolan.org/developers/libbluray.html
25 * git://git.videolan.org/libbluray.git
26 *
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <pthread.h>
38
39 /* libbluray */
40 #include <libbluray/bluray.h>
41 #include <libbluray/bluray-version.h>
42 #include <libbluray/keys.h>
43 #include <libbluray/overlay.h>
44 #include <libbluray/meta_data.h>
45
46 /* xine */
47
48 #define LOG_MODULE "input_bluray"
49 #define LOG_VERBOSE
50
51 /*#define LOG*/
52
53 #define LOGMSG(x...) xine_log (this->stream->xine, XINE_LOG_MSG, "input_bluray: " x);
54
55 #include <xine/xine_internal.h>
56 #include <xine/xineutils.h>
57 #include <xine/input_plugin.h>
58
59 #include "media_helper.h"
60 #include "input_helper.h"
61
62 /* */
63
64 #ifndef MIN
65 # define MIN(a,b) ((a)<(b)?(a):(b))
66 #endif
67 #ifndef MAX
68 # define MAX(a,b) ((a)>(b)?(a):(b))
69 #endif
70
71 #define ALIGNED_UNIT_SIZE 6144
72 #define PKT_SIZE 192
73 #define TICKS_IN_MS 45
74
75 #define MIN_TITLE_LENGTH 180
76
77 #define BLURAY_MNT_PATH "/mnt/bluray"
78 #if defined(__sun)
79 #define BLURAY_PATH "/vol/dev/aliases/cdrom0"
80 #elif defined(__OpenBSD__)
81 #define BLURAY_PATH "/dev/rcd0c"
82 #else
83 #define BLURAY_PATH "/dev/dvd"
84 #endif
85
86 /* */
87
88 typedef struct {
89
90 input_class_t input_class;
91
92 xine_t *xine;
93
94 xine_mrl_t **xine_playlist;
95 int xine_playlist_size;
96
97 /* config */
98 const char *mountpoint;
99 const char *device;
100 const char *language;
101 const char *country;
102 int region;
103 int parental;
104 int skip_mode;
105 } bluray_input_class_t;
106
107 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 2, 4)
108
109 typedef struct {
110 BD_ARGB_BUFFER buf;
111 pthread_mutex_t buf_lock;
112 } XINE_BD_ARGB_BUFFER;
113
osd_buf_lock(BD_ARGB_BUFFER * buf_gen)114 static void osd_buf_lock(BD_ARGB_BUFFER *buf_gen)
115 {
116 XINE_BD_ARGB_BUFFER *buf = (XINE_BD_ARGB_BUFFER*)buf_gen;
117 pthread_mutex_lock(&buf->buf_lock);
118 }
119
osd_buf_unlock(BD_ARGB_BUFFER * buf_gen)120 static void osd_buf_unlock(BD_ARGB_BUFFER *buf_gen)
121 {
122 XINE_BD_ARGB_BUFFER *buf = (XINE_BD_ARGB_BUFFER*)buf_gen;
123 pthread_mutex_unlock(&buf->buf_lock);
124 }
125
osd_buf_init(XINE_BD_ARGB_BUFFER * buf)126 static void osd_buf_init(XINE_BD_ARGB_BUFFER *buf)
127 {
128 buf->buf.lock = osd_buf_lock;
129 buf->buf.unlock = osd_buf_unlock;
130 pthread_mutex_init(&buf->buf_lock, NULL);
131 }
132
osd_buf_destroy(XINE_BD_ARGB_BUFFER * buf)133 static void osd_buf_destroy(XINE_BD_ARGB_BUFFER *buf)
134 {
135 if (buf->buf.lock) {
136 buf->buf.lock = NULL;
137 buf->buf.unlock = NULL;
138 pthread_mutex_destroy(&buf->buf_lock);
139 }
140 }
141
142 #endif /* BLURAY_VERSION >= 0.2.4 */
143
144 typedef struct {
145 input_plugin_t input_plugin;
146
147 bluray_input_class_t *class;
148
149 xine_stream_t *stream;
150 xine_event_queue_t *event_queue;
151 xine_osd_t *osd[2];
152 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 2, 4)
153 XINE_BD_ARGB_BUFFER osd_buf;
154 #endif
155
156 char *mrl;
157 char *disc_root;
158 char *disc_name;
159
160 BLURAY *bdh;
161
162 const BLURAY_DISC_INFO *disc_info;
163 const META_DL *meta_dl; /* disc library meta data */
164
165 int num_title_idx; /* number of relevant playlists */
166 int current_title_idx;
167 int num_titles; /* navigation mode, number of titles in disc index */
168 int current_title; /* navigation mode, title from disc index */
169 BLURAY_TITLE_INFO *title_info;
170 pthread_mutex_t title_info_mutex; /* lock this when accessing title_info outside of input/demux thread */
171 unsigned int current_clip;
172 time_t still_end_time;
173 int pg_stream;
174
175 uint8_t nav_mode : 1;
176 uint8_t error : 1;
177 uint8_t menu_open : 1;
178 uint8_t stream_flushed : 1;
179 uint8_t stream_reset_done : 1;
180 uint8_t demux_action_req : 1;
181 uint8_t end_of_title : 1;
182 uint8_t pg_enable : 1;
183 uint8_t has_video : 1;
184 int mouse_inside_button;
185 } bluray_input_plugin_t;
186
queue_black_frame(bluray_input_plugin_t * this)187 static void queue_black_frame(bluray_input_plugin_t *this)
188 {
189 vo_frame_t *img = NULL;
190
191 if (!_x_lock_port_rewiring(this->class->xine, 0)) {
192 return;
193 }
194
195 img = this->stream->video_out->get_frame(this->stream->video_out,
196 1920, 1080, 16.0/9.0,
197 XINE_IMGFMT_YV12, VO_BOTH_FIELDS);
198
199 if (img) {
200 if (img->format == XINE_IMGFMT_YV12 && img->base[0] && img->base[1] && img->base[2]) {
201 memset(img->base[0], 0x00, img->pitches[0] * img->height);
202 memset(img->base[1], 0x80, img->pitches[1] * img->height / 2);
203 memset(img->base[2], 0x80, img->pitches[2] * img->height / 2);
204 img->duration = 0;
205 img->pts = 0;
206 img->bad_frame = 0;
207 img->draw(img, this->stream);
208
209 this->has_video = 1;
210 }
211 img->free(img);
212 }
213
214 _x_unlock_port_rewiring(this->class->xine);
215 }
216
217 /*
218 * overlay
219 */
220
221 #define PALETTE_INDEX_BACKGROUND 0xff
222
send_num_buttons(bluray_input_plugin_t * this,int n)223 static void send_num_buttons(bluray_input_plugin_t *this, int n)
224 {
225 xine_event_t event;
226 xine_ui_data_t data;
227
228 event.type = XINE_EVENT_UI_NUM_BUTTONS;
229 event.data = &data;
230 event.data_length = sizeof(data);
231 data.num_buttons = n;
232
233 xine_event_send(this->stream, &event);
234 }
235
clear_overlay(xine_osd_t * osd)236 static void clear_overlay(xine_osd_t *osd)
237 {
238 /* palette entry 0xff is background --> can't use xine_osd_clear(). */
239 memset(osd->osd.area, PALETTE_INDEX_BACKGROUND, osd->osd.width * osd->osd.height);
240 osd->osd.x1 = osd->osd.width;
241 osd->osd.y1 = osd->osd.height;
242 osd->osd.x2 = 0;
243 osd->osd.y2 = 0;
244 osd->osd.area_touched = 0;
245 }
246
get_overlay(bluray_input_plugin_t * this,int plane)247 static xine_osd_t *get_overlay(bluray_input_plugin_t *this, int plane)
248 {
249 if (!this->pg_enable) {
250 _x_select_spu_channel(this->stream, -1);
251 }
252 this->stream->video_out->enable_ovl(this->stream->video_out, 1);
253 return this->osd[plane];
254 }
255
close_overlay(bluray_input_plugin_t * this,int plane)256 static void close_overlay(bluray_input_plugin_t *this, int plane)
257 {
258 if (plane < 0) {
259 close_overlay(this, 0);
260 close_overlay(this, 1);
261 return;
262 }
263
264 lprintf("close_overlay(#%d)\n", plane);
265
266 if (plane < 2 && this->osd[plane]) {
267
268 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 2, 4)
269 osd_buf_lock(&this->osd_buf.buf);
270 #endif
271
272 xine_osd_free(this->osd[plane]);
273 this->osd[plane] = NULL;
274
275 #if BLURAY_VERSION < BLURAY_VERSION_CODE(0, 2, 2)
276 if (plane == 1) {
277 send_num_buttons(this, 0);
278 this->menu_open = 0;
279 }
280 #endif
281
282 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 2, 4)
283 free(this->osd_buf.buf.buf[plane]);
284 this->osd_buf.buf.buf[plane] = NULL;
285
286 osd_buf_unlock(&this->osd_buf.buf);
287 #endif
288 }
289 }
290
open_overlay(bluray_input_plugin_t * this,int plane,uint16_t x,uint16_t y,uint16_t w,uint16_t h)291 static void open_overlay(bluray_input_plugin_t *this, int plane, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
292 {
293 lprintf("open_overlay(#%d,%d,%d)\n", plane, w, h);
294
295 if (this->osd[plane]) {
296 close_overlay(this, plane);
297 }
298
299 this->osd[plane] = xine_osd_new(this->stream, x, y, w, h);
300 xine_osd_set_extent(this->osd[plane], w, h);
301 clear_overlay(this->osd[plane]);
302 }
303
draw_bitmap(xine_osd_t * osd,const BD_OVERLAY * const ov)304 static void draw_bitmap(xine_osd_t *osd, const BD_OVERLAY * const ov)
305 {
306 size_t i;
307
308 /* convert and set palette */
309 if (ov->palette) {
310 uint32_t color[256];
311 uint8_t trans[256];
312 for(i = 0; i < 256; i++) {
313 trans[i] = ov->palette[i].T;
314 color[i] = (ov->palette[i].Y << 16) | (ov->palette[i].Cr << 8) | ov->palette[i].Cb;
315 }
316
317 xine_osd_set_palette(osd, color, trans);
318 }
319
320 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 3, 0)
321 if (ov->palette_update_flag)
322 return;
323 #endif
324
325 /* uncompress and draw bitmap */
326 if (ov->img && ov->w > 0 && ov->h > 0) {
327 const BD_PG_RLE_ELEM *rlep = ov->img;
328 size_t pixels = (size_t)ov->w * ov->h;
329 uint8_t *img = malloc(pixels);
330
331 if (img) {
332 for (i = 0; i < pixels; i += rlep->len, rlep++) {
333 memset(img + i, rlep->color, rlep->len);
334 }
335
336 xine_osd_draw_bitmap(osd, img, ov->x, ov->y, ov->w, ov->h, NULL);
337
338 free(img);
339 }
340 }
341 }
342
overlay_proc(void * this_gen,const BD_OVERLAY * const ov)343 static void overlay_proc(void *this_gen, const BD_OVERLAY * const ov)
344 {
345 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
346 xine_osd_t *osd;
347 int64_t vpts = 0;
348
349 if (!this) {
350 return;
351 }
352
353 if (!ov) {
354 /* hide OSD */
355 close_overlay(this, -1);
356 return;
357 }
358
359 if (ov->plane > 1) {
360 return;
361 }
362
363 switch (ov->cmd) {
364 case BD_OVERLAY_INIT:
365 open_overlay(this, ov->plane, ov->x, ov->y, ov->w, ov->h);
366 return;
367 case BD_OVERLAY_CLOSE:
368 close_overlay(this, ov->plane);
369 return;
370 }
371
372 osd = get_overlay(this, ov->plane);
373 if (!osd) {
374 LOGMSG("overlay_proc(): overlay not open (cmd=%d)\n", ov->cmd);
375 return;
376 }
377
378 if (ov->pts > 0) {
379 vpts = ov->pts + this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET);
380 }
381
382 switch (ov->cmd) {
383 case BD_OVERLAY_DRAW:
384 draw_bitmap(osd, ov);
385 return;
386
387 case BD_OVERLAY_WIPE:
388 xine_osd_draw_rect(osd, ov->x, ov->y, ov->x + ov->w - 1, ov->y + ov->h - 1, PALETTE_INDEX_BACKGROUND, 1);
389 return;
390
391 case BD_OVERLAY_CLEAR:
392 clear_overlay(osd);
393 return;
394
395 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 3, 0)
396 case BD_OVERLAY_HIDE:
397 osd->osd.area_touched = 0; /* will be hiden at next commit time */
398 break;
399 #endif
400
401 case BD_OVERLAY_FLUSH:
402 if (!osd->osd.area_touched) {
403 xine_osd_hide(osd, vpts);
404 } else {
405 xine_osd_show(osd, vpts);
406 }
407 #if BLURAY_VERSION < BLURAY_VERSION_CODE(0, 2, 2)
408 if (ov->plane == 1) {
409 this->menu_open = !!osd->osd.area_touched;
410 send_num_buttons(this, !!osd->osd.area_touched);
411 }
412 #endif
413 return;
414
415 default:
416 lprintf("unknown overlay command %d\n", ov->cmd);
417 return;
418 }
419 }
420
421 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 2, 4)
422
open_argb_overlay(bluray_input_plugin_t * this,int plane,uint16_t x,uint16_t y,uint16_t w,uint16_t h)423 static void open_argb_overlay(bluray_input_plugin_t *this, int plane, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
424 {
425 lprintf("open_argb_overlay(#%d,%d,%d)\n", plane, w, h);
426
427 open_overlay(this, plane, x, y, w, h);
428
429 if (xine_osd_get_capabilities(this->osd[plane]) & XINE_OSD_CAP_ARGB_LAYER) {
430 this->osd_buf.buf.width = w;
431 this->osd_buf.buf.height = h;
432 this->osd_buf.buf.buf[plane] = calloc(sizeof(uint32_t), (size_t)w * h);
433 } else {
434 LOGMSG("open_argb_overlay() failed: video driver does not support ARGB overlays.\n");
435 }
436 }
437
get_argb_overlay(bluray_input_plugin_t * this,int plane)438 static xine_osd_t *get_argb_overlay(bluray_input_plugin_t *this, int plane)
439 {
440 if (!this->osd_buf.buf.buf[plane]) {
441 return NULL;
442 }
443 return get_overlay(this, plane);
444 }
445
argb_overlay_proc(void * this_gen,const BD_ARGB_OVERLAY * const ov)446 static void argb_overlay_proc(void *this_gen, const BD_ARGB_OVERLAY * const ov)
447 {
448 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
449 xine_osd_t *osd;
450 int64_t vpts = 0;
451
452 if (!this) {
453 return;
454 }
455
456 if (!ov) {
457 /* hide OSD */
458 close_overlay(this, -1);
459 return;
460 }
461
462 if (ov->pts > 0) {
463 vpts = ov->pts + this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET);
464 }
465
466 switch (ov->cmd) {
467 case BD_ARGB_OVERLAY_INIT:
468 open_argb_overlay(this, ov->plane, 0, 0, ov->w, ov->h);
469 return;
470
471 case BD_ARGB_OVERLAY_CLOSE:
472 close_overlay(this, ov->plane);
473 return;
474 }
475
476 osd = get_argb_overlay(this, ov->plane);
477 if (!osd) {
478 LOGMSG("argb_overlay_proc(): ARGB overlay not open (cmd=%d)\n", ov->cmd);
479 return;
480 }
481
482 switch (ov->cmd) {
483 case BD_ARGB_OVERLAY_FLUSH:
484
485 osd_buf_lock(&this->osd_buf.buf);
486
487 xine_osd_set_argb_buffer(osd, this->osd_buf.buf.buf[ov->plane],
488 this->osd_buf.buf.dirty[ov->plane].x0,
489 this->osd_buf.buf.dirty[ov->plane].y0,
490 this->osd_buf.buf.dirty[ov->plane].x1 - this->osd_buf.buf.dirty[ov->plane].x0 + 1,
491 this->osd_buf.buf.dirty[ov->plane].y1 - this->osd_buf.buf.dirty[ov->plane].y0 + 1);
492
493 xine_osd_show(osd, vpts);
494
495 osd_buf_unlock(&this->osd_buf.buf);
496 return;
497 case BD_ARGB_OVERLAY_DRAW:
498 /* nothing to do, DRI in use */
499 break;
500
501 default:
502 lprintf("unknown ARGB overlay command %d\n", ov->cmd);
503 return;
504 }
505 }
506
507 #endif /* BLURAY_VERSION >= 0.2.4 */
508
509 /*
510 * stream info
511 */
512
update_stream_info(bluray_input_plugin_t * this)513 static void update_stream_info(bluray_input_plugin_t *this)
514 {
515 if (this->title_info) {
516 /* set stream info */
517 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_COUNT, this->title_info->angle_count);
518 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER, bd_get_current_angle(this->bdh));
519 _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_CHAPTERS, this->title_info->chapter_count > 0);
520 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_COUNT, this->title_info->chapter_count);
521 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER, bd_get_current_chapter(this->bdh) + 1);
522 }
523 }
524
update_title_name(bluray_input_plugin_t * this)525 static void update_title_name(bluray_input_plugin_t *this)
526 {
527 char title_name[64] = "";
528 xine_ui_data_t udata;
529 xine_event_t uevent = {
530 .type = XINE_EVENT_UI_SET_TITLE,
531 .stream = this->stream,
532 .data = &udata,
533 .data_length = sizeof(udata)
534 };
535
536 /* check disc library metadata */
537 if (this->meta_dl) {
538 unsigned i;
539 for (i = 0; i < this->meta_dl->toc_count; i++)
540 if (this->meta_dl->toc_entries[i].title_number == (unsigned)this->current_title)
541 if (this->meta_dl->toc_entries[i].title_name)
542 if (strlen(this->meta_dl->toc_entries[i].title_name) > 2) {
543 strlcpy(title_name, this->meta_dl->toc_entries[i].title_name, sizeof(title_name));
544 }
545 }
546
547 /* title name */
548 if (title_name[0]) {
549 } else if (this->current_title == BLURAY_TITLE_TOP_MENU) {
550 strcpy(title_name, "Top Menu");
551 } else if (this->current_title == BLURAY_TITLE_FIRST_PLAY) {
552 strcpy(title_name, "First Play");
553 } else if (this->nav_mode) {
554 snprintf(title_name, sizeof(title_name), "Title %d/%d",
555 this->current_title, this->num_titles);
556 } else {
557 snprintf(title_name, sizeof(title_name), "Title %d/%d",
558 this->current_title_idx + 1, this->num_title_idx);
559 }
560
561 /* disc name */
562 if (this->disc_name && this->disc_name[0]) {
563 udata.str_len = snprintf(udata.str, sizeof(udata.str), "%s, %s",
564 this->disc_name, title_name);
565 } else {
566 udata.str_len = snprintf(udata.str, sizeof(udata.str), "%s",
567 title_name);
568 }
569
570 _x_meta_info_set(this->stream, XINE_META_INFO_TITLE, udata.str);
571
572 xine_event_send(this->stream, &uevent);
573 }
574
update_title_info(bluray_input_plugin_t * this,int playlist_id)575 static void update_title_info(bluray_input_plugin_t *this, int playlist_id)
576 {
577 /* update title_info */
578
579 pthread_mutex_lock(&this->title_info_mutex);
580
581 if (this->title_info)
582 bd_free_title_info(this->title_info);
583
584 if (playlist_id < 0)
585 this->title_info = bd_get_title_info(this->bdh, this->current_title_idx, 0);
586 else
587 this->title_info = bd_get_playlist_info(this->bdh, playlist_id, 0);
588
589 pthread_mutex_unlock(&this->title_info_mutex);
590
591 if (!this->title_info) {
592 LOGMSG("bd_get_title_info(%d) failed\n", this->current_title_idx);
593 return;
594 }
595
596 /* calculate and set stream rate */
597
598 uint64_t rate = bd_get_title_size(this->bdh) * UINT64_C(8) // bits
599 * INT64_C(90000)
600 / (uint64_t)(this->title_info->duration);
601 _x_stream_info_set(this->stream, XINE_STREAM_INFO_BITRATE, rate);
602
603 /* set stream info */
604
605 if (this->nav_mode) {
606 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_TITLE_COUNT, this->num_titles);
607 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_TITLE_NUMBER, this->current_title);
608 } else {
609 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_TITLE_COUNT, this->num_title_idx);
610 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_TITLE_NUMBER, this->current_title_idx + 1);
611 }
612
613 update_stream_info(this);
614
615 /* set title name */
616
617 update_title_name(this);
618 }
619
620 /*
621 * libbluray event handling
622 */
623
fifos_wait(bluray_input_plugin_t * this)624 static void fifos_wait(bluray_input_plugin_t *this)
625 {
626 if (!this->stream)
627 return;
628
629 if (this->stream->video_fifo) {
630 buf_element_t *buf = this->stream->video_fifo->buffer_pool_alloc(this->stream->video_fifo);
631 if (buf) {
632 buf->type = BUF_CONTROL_FLUSH_DECODER;
633 this->stream->video_fifo->put(this->stream->video_fifo, buf);
634 }
635 }
636
637 time_t start = time(NULL);
638
639 while (1) {
640 int vb = -1, ab = -1, vf = -1, af = -1;
641 _x_query_buffer_usage(this->stream, &vb, &ab, &vf, &af);
642
643 if (vb <= 0 && ab <= 0 && vf <= 0 && af <= 0)
644 break;
645
646 xine_usec_sleep(5000);
647
648 if (time(NULL) > start + 10) {
649 LOGMSG("fifos_wait timeout");
650 break;
651 }
652 }
653 }
654
655
stream_flush(bluray_input_plugin_t * this)656 static void stream_flush(bluray_input_plugin_t *this)
657 {
658 if (!this || this->stream_flushed || !this->stream)
659 return;
660
661 lprintf("Stream flush\n");
662
663 this->stream_flushed = 1;
664
665 xine_event_t event = {
666 .type = XINE_EVENT_END_OF_CLIP,
667 .stream = this->stream,
668 .data = NULL,
669 .data_length = 0,
670 };
671 xine_event_send (this->stream, &event);
672
673 this->demux_action_req = 1;
674 }
675
stream_reset(bluray_input_plugin_t * this)676 static void stream_reset(bluray_input_plugin_t *this)
677 {
678 if (!this || this->stream_reset_done || !this->stream)
679 return;
680
681 lprintf("Stream reset\n");
682
683 xine_event_t event = {
684 .type = XINE_EVENT_PIDS_CHANGE,
685 .stream = this->stream,
686 .data = NULL,
687 .data_length = 0,
688 };
689
690 if (!this->end_of_title) {
691 _x_demux_flush_engine(this->stream);
692 }
693
694 xine_event_send (this->stream, &event);
695
696 this->demux_action_req = 1;
697 this->stream_reset_done = 1;
698 }
699
wait_secs(bluray_input_plugin_t * this,unsigned seconds)700 static void wait_secs(bluray_input_plugin_t *this, unsigned seconds)
701 {
702 stream_flush(this);
703
704 if (this->still_end_time) {
705 if (time(NULL) >= this->still_end_time) {
706 lprintf("pause end\n");
707 this->still_end_time = 0;
708 bd_read_skip_still(this->bdh);
709 stream_reset(this);
710 return;
711 }
712 }
713
714 else if (seconds) {
715 if (seconds > 300) {
716 seconds = 300;
717 }
718
719 lprintf("still image, pause for %d seconds\n", seconds);
720 this->still_end_time = time(NULL) + seconds;
721 }
722
723 xine_usec_sleep(40*1000);
724 }
725
update_spu_channel(bluray_input_plugin_t * this,int channel)726 static void update_spu_channel(bluray_input_plugin_t *this, int channel)
727 {
728 if (this->stream->video_fifo) {
729 buf_element_t *buf = this->stream->video_fifo->buffer_pool_alloc(this->stream->video_fifo);
730 buf->type = BUF_CONTROL_SPU_CHANNEL;
731 buf->decoder_info[0] = channel;
732 buf->decoder_info[1] = channel;
733 buf->decoder_info[2] = channel;
734
735 this->stream->video_fifo->put(this->stream->video_fifo, buf);
736 }
737 }
738
update_audio_channel(bluray_input_plugin_t * this,int channel)739 static void update_audio_channel(bluray_input_plugin_t *this, int channel)
740 {
741 if (this->stream->audio_fifo) {
742 buf_element_t *buf = this->stream->audio_fifo->buffer_pool_alloc(this->stream->audio_fifo);
743 buf->type = BUF_CONTROL_AUDIO_CHANNEL;
744 buf->decoder_info[0] = channel;
745
746 this->stream->audio_fifo->put(this->stream->audio_fifo, buf);
747 }
748 }
749
handle_libbluray_event(bluray_input_plugin_t * this,BD_EVENT ev)750 static void handle_libbluray_event(bluray_input_plugin_t *this, BD_EVENT ev)
751 {
752 switch ((bd_event_e)ev.event) {
753
754 case BD_EVENT_NONE:
755 break;
756
757 case BD_EVENT_ERROR:
758 lprintf("BD_EVENT_ERROR\n");
759 _x_message (this->stream, XINE_MSG_GENERAL_WARNING,
760 "Error playing BluRay disc", NULL);
761 this->error = 1;
762 return;
763
764 case BD_EVENT_READ_ERROR:
765 LOGMSG("BD_EVENT_READ_ERROR\n");
766 return;
767
768 case BD_EVENT_ENCRYPTED:
769 lprintf("BD_EVENT_ENCRYPTED\n");
770 _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE,
771 "Media stream scrambled/encrypted", NULL);
772 this->error = 1;
773 return;
774
775 /* sound effects */
776 #if BLURAY_VERSION >= 202
777 case BD_EVENT_SOUND_EFFECT:
778 lprintf("BD_EVENT_SOUND_EFFECT %d\n", ev.param);
779 break;
780 #endif
781
782 /* playback control */
783
784 case BD_EVENT_SEEK:
785 lprintf("BD_EVENT_SEEK\n");
786 this->still_end_time = 0;
787 stream_reset(this);
788 break;
789
790 case BD_EVENT_STILL_TIME:
791 wait_secs(this, ev.param);
792 break;
793
794 case BD_EVENT_STILL:
795 lprintf("BD_EVENT_STILL %d\n", ev.param);
796 unsigned int paused = _x_get_fine_speed(this->stream) == XINE_SPEED_PAUSE;
797 if (paused != ev.param) {
798 _x_set_fine_speed(this->stream, ev.param ? XINE_SPEED_PAUSE : XINE_SPEED_NORMAL);
799 }
800 break;
801
802 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 3, 0)
803 case BD_EVENT_IDLE:
804 xine_usec_sleep(10000);
805 break;
806 #endif
807
808 /* playback position */
809
810 case BD_EVENT_ANGLE:
811 lprintf("BD_EVENT_ANGLE_NUMBER %d\n", ev.param);
812 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER, ev.param);
813 break;
814
815 case BD_EVENT_END_OF_TITLE:
816 lprintf("BD_EVENT_END_OF_TITLE\n");
817 stream_flush(this);
818 fifos_wait(this);
819 this->end_of_title = 1;
820 break;
821
822 case BD_EVENT_TITLE:
823 if (this->nav_mode) {
824 lprintf("BD_EVENT_TITLE %d\n", ev.param);
825 this->current_title = ev.param;
826 }
827 break;
828
829 case BD_EVENT_PLAYLIST:
830 lprintf("BD_EVENT_PLAYLIST %d\n", ev.param);
831 if (!this->nav_mode) {
832 this->current_title_idx = bd_get_current_title(this->bdh);
833 }
834 this->current_clip = 0;
835 update_title_info(this, ev.param);
836 stream_reset(this);
837 this->end_of_title = 0;
838 break;
839
840 case BD_EVENT_PLAYITEM:
841 lprintf("BD_EVENT_PLAYITEM %d\n", ev.param);
842 this->current_clip = ev.param;
843 this->still_end_time = 0;
844 break;
845
846 case BD_EVENT_CHAPTER:
847 lprintf("BD_EVENT_CHAPTER %d\n", ev.param);
848 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER, ev.param);
849 break;
850
851 /* stream selection */
852
853 case BD_EVENT_AUDIO_STREAM:
854 lprintf("BD_EVENT_AUDIO_STREAM %d\n", ev.param);
855 if (ev.param < 32) {
856 update_audio_channel(this, ev.param - 1);
857 } else {
858 update_audio_channel(this, 0);
859 }
860 break;
861
862 case BD_EVENT_PG_TEXTST:
863 lprintf("BD_EVENT_PG_TEXTST %s\n", ev.param ? "ON" : "OFF");
864 this->pg_enable = !!ev.param;
865 update_spu_channel(this, this->pg_enable ? this->pg_stream : -1);
866 break;
867
868 case BD_EVENT_PG_TEXTST_STREAM:
869 lprintf("BD_EVENT_PG_TEXTST_STREAM %d\n", ev.param);
870 if (ev.param < 64) {
871 this->pg_stream = ev.param - 1;
872 } else {
873 this->pg_stream = -1;
874 }
875 if (this->pg_enable) {
876 update_spu_channel(this, this->pg_stream);
877 }
878 break;
879
880 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 2, 2)
881 case BD_EVENT_POPUP:
882 lprintf("BD_EVENT_POPUP %d\n", ev.param);
883 break;
884
885 case BD_EVENT_MENU:
886 this->menu_open = !!ev.param;
887 send_num_buttons(this, ev.param);
888 break;
889 #endif
890
891 default:
892 lprintf("unhandled libbluray event %d [param %d]\n", ev.event, ev.param);
893 break;
894 }
895 }
896
handle_libbluray_events(bluray_input_plugin_t * this)897 static void handle_libbluray_events(bluray_input_plugin_t *this)
898 {
899 BD_EVENT ev;
900 while (bd_get_event(this->bdh, &ev)) {
901 handle_libbluray_event(this, ev);
902 if (this->error || ev.event == BD_EVENT_NONE || ev.event == BD_EVENT_ERROR)
903 break;
904 }
905 }
906
907 /*
908 * xine event handling
909 */
910
open_title(bluray_input_plugin_t * this,int title_idx)911 static int open_title (bluray_input_plugin_t *this, int title_idx)
912 {
913 if (bd_select_title(this->bdh, title_idx) <= 0) {
914 LOGMSG("bd_select_title(%d) failed\n", title_idx);
915 return 0;
916 }
917
918 this->current_title_idx = title_idx;
919
920 update_title_info(this, -1);
921
922 #ifdef LOG
923 int ms = this->title_info->duration / INT64_C(90);
924 lprintf("Opened title %d. Length %"PRId64" bytes / %02d:%02d:%02d.%03d\n",
925 this->current_title_idx, bd_get_title_size(this->bdh),
926 ms / 3600000, (ms % 3600000 / 60000), (ms % 60000) / 1000, ms % 1000);
927 #endif
928
929 return 1;
930 }
931
send_mouse_enter_leave_event(bluray_input_plugin_t * this,int direction)932 static void send_mouse_enter_leave_event(bluray_input_plugin_t *this, int direction)
933 {
934 if (direction != this->mouse_inside_button) {
935 xine_event_t event;
936 xine_spu_button_t spu_event;
937
938 spu_event.direction = direction;
939 spu_event.button = 1;
940
941 event.type = XINE_EVENT_SPU_BUTTON;
942 event.stream = this->stream;
943 event.data = &spu_event;
944 event.data_length = sizeof(spu_event);
945 xine_event_send(this->stream, &event);
946
947 this->mouse_inside_button = direction;
948 }
949 }
950
handle_events(bluray_input_plugin_t * this)951 static void handle_events(bluray_input_plugin_t *this)
952 {
953 xine_event_t *event;
954
955 if (!this->event_queue)
956 return;
957
958 while (NULL != (event = xine_event_get(this->event_queue))) {
959
960 if (!this->bdh || !this->title_info) {
961 xine_event_free(event);
962 return;
963 }
964
965 int64_t pts = xine_get_current_vpts(this->stream) -
966 this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET);
967
968 if (this->menu_open) {
969 switch (event->type) {
970 case XINE_EVENT_INPUT_LEFT: bd_user_input(this->bdh, pts, BD_VK_LEFT); break;
971 case XINE_EVENT_INPUT_RIGHT: bd_user_input(this->bdh, pts, BD_VK_RIGHT); break;
972 }
973 } else {
974 switch (event->type) {
975
976 case XINE_EVENT_INPUT_LEFT:
977 lprintf("XINE_EVENT_INPUT_LEFT: previous title\n");
978 if (!this->nav_mode) {
979 open_title(this, MAX(0, this->current_title_idx - 1));
980 } else {
981 bd_play_title(this->bdh, MAX(1, this->current_title - 1));
982 }
983 stream_reset(this);
984 break;
985
986 case XINE_EVENT_INPUT_RIGHT:
987 lprintf("XINE_EVENT_INPUT_RIGHT: next title\n");
988 if (!this->nav_mode) {
989 open_title(this, MIN(this->num_title_idx - 1, this->current_title_idx + 1));
990 } else {
991 bd_play_title(this->bdh, MIN(this->num_titles, this->current_title + 1));
992 }
993 stream_reset(this);
994 break;
995 }
996 }
997
998 switch (event->type) {
999
1000 case XINE_EVENT_INPUT_MOUSE_BUTTON: {
1001 xine_input_data_t *input = event->data;
1002 lprintf("mouse click: button %d at (%d,%d)\n", input->button, input->x, input->y);
1003 if (input->button == 1) {
1004 bd_mouse_select(this->bdh, pts, input->x, input->y);
1005 bd_user_input(this->bdh, pts, BD_VK_MOUSE_ACTIVATE);
1006 send_mouse_enter_leave_event(this, 0);
1007 }
1008 break;
1009 }
1010
1011 case XINE_EVENT_INPUT_MOUSE_MOVE: {
1012 xine_input_data_t *input = event->data;
1013 if (bd_mouse_select(this->bdh, pts, input->x, input->y) > 0) {
1014 send_mouse_enter_leave_event(this, 1);
1015 } else {
1016 send_mouse_enter_leave_event(this, 0);
1017 }
1018 break;
1019 }
1020
1021 case XINE_EVENT_INPUT_MENU1:
1022 if (!this->disc_info->top_menu_supported) {
1023 _x_message (this->stream, XINE_MSG_GENERAL_WARNING,
1024 "Can't open Top Menu",
1025 "Top Menu title not supported", NULL);
1026 }
1027 bd_menu_call(this->bdh, pts);
1028 break;
1029
1030 case XINE_EVENT_INPUT_MENU2: bd_user_input(this->bdh, pts, BD_VK_POPUP); break;
1031 case XINE_EVENT_INPUT_UP: bd_user_input(this->bdh, pts, BD_VK_UP); break;
1032 case XINE_EVENT_INPUT_DOWN: bd_user_input(this->bdh, pts, BD_VK_DOWN); break;
1033 case XINE_EVENT_INPUT_SELECT: bd_user_input(this->bdh, pts, BD_VK_ENTER); break;
1034 case XINE_EVENT_INPUT_NUMBER_0: bd_user_input(this->bdh, pts, BD_VK_0); break;
1035 case XINE_EVENT_INPUT_NUMBER_1: bd_user_input(this->bdh, pts, BD_VK_1); break;
1036 case XINE_EVENT_INPUT_NUMBER_2: bd_user_input(this->bdh, pts, BD_VK_2); break;
1037 case XINE_EVENT_INPUT_NUMBER_3: bd_user_input(this->bdh, pts, BD_VK_3); break;
1038 case XINE_EVENT_INPUT_NUMBER_4: bd_user_input(this->bdh, pts, BD_VK_4); break;
1039 case XINE_EVENT_INPUT_NUMBER_5: bd_user_input(this->bdh, pts, BD_VK_5); break;
1040 case XINE_EVENT_INPUT_NUMBER_6: bd_user_input(this->bdh, pts, BD_VK_6); break;
1041 case XINE_EVENT_INPUT_NUMBER_7: bd_user_input(this->bdh, pts, BD_VK_7); break;
1042 case XINE_EVENT_INPUT_NUMBER_8: bd_user_input(this->bdh, pts, BD_VK_8); break;
1043 case XINE_EVENT_INPUT_NUMBER_9: bd_user_input(this->bdh, pts, BD_VK_9); break;
1044
1045 case XINE_EVENT_INPUT_NEXT: {
1046 switch (this->class->skip_mode) {
1047 case 0: /* skip by chapter */
1048 bd_seek_chapter(this->bdh, bd_get_current_chapter(this->bdh) + 1);
1049 update_stream_info(this);
1050 break;
1051 case 1: /* skip by title */
1052 if (!this->nav_mode) {
1053 open_title(this, MIN(this->num_title_idx - 1, this->current_title_idx + 1));
1054 } else {
1055 bd_play_title(this->bdh, MIN(this->num_titles, this->current_title + 1));
1056 }
1057 break;
1058 }
1059 stream_reset(this);
1060 break;
1061 }
1062
1063 case XINE_EVENT_INPUT_PREVIOUS: {
1064 switch (this->class->skip_mode) {
1065 case 0: /* skip by chapter */
1066 bd_seek_chapter(this->bdh, MAX(0, ((int)bd_get_current_chapter(this->bdh)) - 1));
1067 update_stream_info(this);
1068 break;
1069 case 1: /* skip by title */
1070 if (!this->nav_mode) {
1071 open_title(this, MAX(0, this->current_title_idx - 1));
1072 } else {
1073 bd_play_title(this->bdh, MAX(1, this->current_title - 1));
1074 }
1075 break;
1076 }
1077 stream_reset(this);
1078 break;
1079 }
1080
1081 case XINE_EVENT_INPUT_ANGLE_NEXT: {
1082 unsigned curr_angle = bd_get_current_angle(this->bdh);
1083 unsigned angle = MIN(8, curr_angle + 1);
1084 lprintf("XINE_EVENT_INPUT_ANGLE_NEXT: set angle %d --> %d\n", curr_angle, angle);
1085 bd_seamless_angle_change(this->bdh, angle);
1086 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER, bd_get_current_angle(this->bdh));
1087 break;
1088 }
1089
1090 case XINE_EVENT_INPUT_ANGLE_PREVIOUS: {
1091 unsigned curr_angle = bd_get_current_angle(this->bdh);
1092 unsigned angle = curr_angle ? curr_angle - 1 : 0;
1093 lprintf("XINE_EVENT_INPUT_ANGLE_PREVIOUS: set angle %d --> %d\n", curr_angle, angle);
1094 bd_seamless_angle_change(this->bdh, angle);
1095 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER, bd_get_current_angle(this->bdh));
1096 break;
1097 }
1098 }
1099
1100 xine_event_free(event);
1101 }
1102 }
1103
1104 /*
1105 * xine plugin interface
1106 */
1107
bluray_plugin_get_capabilities(input_plugin_t * this_gen)1108 static uint32_t bluray_plugin_get_capabilities (input_plugin_t *this_gen)
1109 {
1110 (void)this_gen;
1111 return INPUT_CAP_SEEKABLE |
1112 INPUT_CAP_TIME_SEEKABLE |
1113 INPUT_CAP_BLOCK |
1114 INPUT_CAP_AUDIOLANG |
1115 INPUT_CAP_SPULANG |
1116 INPUT_CAP_CHAPTERS;
1117 }
1118
1119 #define CHECK_READ_INTERRUPT \
1120 do { \
1121 if (this->demux_action_req) { \
1122 this->demux_action_req = 0; \
1123 errno = EAGAIN; \
1124 return -1; \
1125 } \
1126 if (_x_action_pending(this->stream)) { \
1127 errno = EINTR; \
1128 return -1; \
1129 } \
1130 } while (0)
1131
bluray_plugin_read(input_plugin_t * this_gen,void * buf,off_t len)1132 static off_t bluray_plugin_read (input_plugin_t *this_gen, void *buf, off_t len)
1133 {
1134 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1135 off_t result;
1136
1137 if (!this || !this->bdh || len < 0 || this->error)
1138 return -1;
1139
1140 if (!this->has_video) {
1141 queue_black_frame(this);
1142 }
1143
1144 handle_events(this);
1145 CHECK_READ_INTERRUPT;
1146
1147 if (this->nav_mode) {
1148 do {
1149 BD_EVENT ev;
1150 result = bd_read_ext (this->bdh, (unsigned char *)buf, len, &ev);
1151 handle_libbluray_event(this, ev);
1152 CHECK_READ_INTERRUPT;
1153
1154 if (result == 0) {
1155 handle_events(this);
1156 CHECK_READ_INTERRUPT;
1157 #if 0
1158 if (ev.event == BD_EVENT_NONE) {
1159 if (_x_action_pending(this->stream)) {
1160 break;
1161 }
1162 }
1163 #endif
1164 }
1165 } while (!this->error && result == 0);
1166
1167 } else {
1168 result = bd_read (this->bdh, (unsigned char *)buf, len);
1169 handle_libbluray_events(this);
1170 }
1171
1172 if (result < 0) {
1173 LOGMSG("bd_read() failed: %s (%d of %d)\n", strerror(errno), (int)result, (int)len);
1174 }
1175
1176 if (result > 0) {
1177 this->stream_flushed = 0;
1178 this->stream_reset_done = 0;
1179 }
1180
1181 return result;
1182 }
1183
bluray_plugin_read_block(input_plugin_t * this_gen,fifo_buffer_t * fifo,off_t todo)1184 static buf_element_t *bluray_plugin_read_block (input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t todo)
1185 {
1186 buf_element_t *buf;
1187
1188 if (todo > ALIGNED_UNIT_SIZE)
1189 todo = ALIGNED_UNIT_SIZE;
1190
1191 buf = fifo->buffer_pool_size_alloc (fifo, todo);
1192 if (todo > (off_t)buf->max_size)
1193 todo = buf->max_size;
1194
1195 if (todo > 0) {
1196 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1197
1198 buf->size = bluray_plugin_read(this_gen, (char*)buf->mem, todo);
1199 buf->type = BUF_DEMUX_BLOCK;
1200
1201 if (buf->size > 0) {
1202 buf->extra_info->total_time = this->title_info->duration / 90;
1203 return buf;
1204 }
1205 }
1206
1207 buf->free_buffer (buf);
1208 return NULL;
1209 }
1210
bluray_plugin_seek(input_plugin_t * this_gen,off_t offset,int origin)1211 static off_t bluray_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin)
1212 {
1213 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1214
1215 if (!this || !this->bdh)
1216 return -1;
1217 if (this->still_end_time)
1218 return offset;
1219
1220 /* convert relative seeks to absolute */
1221
1222 if (origin == SEEK_CUR) {
1223 offset = bd_tell(this->bdh) + offset;
1224 }
1225 else if (origin == SEEK_END) {
1226 if (offset < (off_t)bd_get_title_size(this->bdh))
1227 offset = bd_get_title_size(this->bdh) - offset;
1228 else
1229 offset = 0;
1230 }
1231
1232 lprintf("bluray_plugin_seek() seeking to %lld\n", (long long)offset);
1233
1234 return bd_seek (this->bdh, offset);
1235 }
1236
bluray_plugin_seek_time(input_plugin_t * this_gen,int time_offset,int origin)1237 static off_t bluray_plugin_seek_time (input_plugin_t *this_gen, int time_offset, int origin)
1238 {
1239 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1240
1241 if (!this || !this->bdh)
1242 return -1;
1243
1244 if (this->still_end_time)
1245 return bd_tell(this->bdh);
1246
1247 /* convert relative seeks to absolute */
1248
1249 if (origin == SEEK_CUR) {
1250 time_offset += this_gen->get_current_time(this_gen);
1251 }
1252 else if (origin == SEEK_END) {
1253
1254 pthread_mutex_lock(&this->title_info_mutex);
1255
1256 if (!this->title_info) {
1257 pthread_mutex_unlock(&this->title_info_mutex);
1258 return -1;
1259 }
1260
1261 int duration = this->title_info->duration / 90;
1262 if (time_offset < duration)
1263 time_offset = duration - time_offset;
1264 else
1265 time_offset = 0;
1266
1267 pthread_mutex_unlock(&this->title_info_mutex);
1268 }
1269
1270 lprintf("bluray_plugin_seek_time() seeking to %d.%03ds\n", time_offset / 1000, time_offset % 1000);
1271
1272 return bd_seek_time(this->bdh, time_offset * INT64_C(90));
1273 }
1274
bluray_plugin_get_current_pos(input_plugin_t * this_gen)1275 static off_t bluray_plugin_get_current_pos (input_plugin_t *this_gen)
1276 {
1277 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1278
1279 return this->bdh ? bd_tell(this->bdh) : 0;
1280 }
1281
bluray_plugin_get_current_time(input_plugin_t * this_gen)1282 static int bluray_plugin_get_current_time (input_plugin_t *this_gen)
1283 {
1284 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1285
1286 return this->bdh ? (int)(bd_tell_time(this->bdh) / UINT64_C(90)) : -1;
1287 }
1288
bluray_plugin_get_length(input_plugin_t * this_gen)1289 static off_t bluray_plugin_get_length (input_plugin_t *this_gen)
1290 {
1291 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1292
1293 return this->bdh ? (off_t)bd_get_title_size(this->bdh) : (off_t)-1;
1294 }
1295
bluray_plugin_get_blocksize(input_plugin_t * this_gen)1296 static uint32_t bluray_plugin_get_blocksize (input_plugin_t *this_gen)
1297 {
1298 (void)this_gen;
1299 return ALIGNED_UNIT_SIZE;
1300 }
1301
bluray_plugin_get_mrl(input_plugin_t * this_gen)1302 static const char* bluray_plugin_get_mrl (input_plugin_t *this_gen)
1303 {
1304 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1305
1306 return this->mrl;
1307 }
1308
get_audio_lang(bluray_input_plugin_t * this,void * data)1309 static int get_audio_lang (bluray_input_plugin_t *this, void *data)
1310 {
1311 /*
1312 * audio track language:
1313 * - channel number can be mpeg-ts PID (0x1100 ... 0x11ff)
1314 */
1315
1316 unsigned int current_clip = this->current_clip; /* can change any time */
1317
1318 if (this->title_info && current_clip < this->title_info->clip_count) {
1319 BLURAY_CLIP_INFO *clip = &this->title_info->clips[current_clip];
1320 int channel;
1321
1322 memcpy(&channel, data, sizeof(channel));
1323
1324 if (channel >= 0 && channel < clip->audio_stream_count) {
1325 memcpy(data, clip->audio_streams[channel].lang, 4);
1326 return INPUT_OPTIONAL_SUCCESS;
1327 }
1328
1329 /* search by pid */
1330 int i;
1331 for (i = 0; i < clip->audio_stream_count; i++) {
1332 if (channel == clip->audio_streams[i].pid) {
1333 memcpy(data, clip->audio_streams[i].lang, 4);
1334 return INPUT_OPTIONAL_SUCCESS;
1335 }
1336 }
1337 }
1338
1339 return INPUT_OPTIONAL_UNSUPPORTED;
1340 }
1341
get_spu_lang(bluray_input_plugin_t * this,void * data)1342 static int get_spu_lang (bluray_input_plugin_t *this, void *data)
1343 {
1344 /*
1345 * SPU track language:
1346 * - channel number can be mpeg-ts PID (0x1200 ... 0x12ff)
1347 */
1348
1349 unsigned int current_clip = this->current_clip; /* can change any time */
1350
1351 if (this->title_info && current_clip < this->title_info->clip_count) {
1352 BLURAY_CLIP_INFO *clip = &this->title_info->clips[current_clip];
1353 int channel;
1354
1355 memcpy(&channel, data, sizeof(channel));
1356
1357 if (channel >= 0 && channel < clip->pg_stream_count) {
1358 memcpy(data, clip->pg_streams[channel].lang, 4);
1359 return INPUT_OPTIONAL_SUCCESS;
1360 }
1361
1362 /* search by pid */
1363 int i;
1364 for (i = 0; i < clip->pg_stream_count; i++) {
1365 if (channel == clip->pg_streams[i].pid) {
1366 memcpy(data, clip->pg_streams[i].lang, 4);
1367 return INPUT_OPTIONAL_SUCCESS;
1368 }
1369 }
1370 }
1371
1372 return INPUT_OPTIONAL_UNSUPPORTED;
1373 }
1374
get_optional_data_impl(bluray_input_plugin_t * this,void * data,int data_type)1375 static int get_optional_data_impl (bluray_input_plugin_t *this, void *data, int data_type)
1376 {
1377 int r;
1378
1379 switch (data_type) {
1380
1381 case INPUT_OPTIONAL_DATA_DEMUXER:
1382 if (data)
1383 *(const char **)data = "mpeg-ts";
1384 return INPUT_OPTIONAL_SUCCESS;
1385
1386 case INPUT_OPTIONAL_DATA_AUDIOLANG:
1387 r = get_audio_lang(this, data);
1388 return r;
1389
1390 case INPUT_OPTIONAL_DATA_SPULANG:
1391 r = get_spu_lang(this, data);
1392 return r;
1393
1394 case INPUT_OPTIONAL_DATA_DURATION:
1395 if (data && this->title_info) {
1396 uint32_t duration = (uint32_t)(this->title_info->duration / UINT64_C(90));
1397 memcpy (data, &duration, sizeof (duration));
1398 return INPUT_OPTIONAL_SUCCESS;
1399 }
1400 return INPUT_OPTIONAL_UNSUPPORTED;
1401
1402 default:
1403 return INPUT_OPTIONAL_UNSUPPORTED;
1404 }
1405
1406 return INPUT_OPTIONAL_UNSUPPORTED;
1407 }
1408
bluray_plugin_get_optional_data(input_plugin_t * this_gen,void * data,int data_type)1409 static int bluray_plugin_get_optional_data (input_plugin_t *this_gen, void *data, int data_type)
1410 {
1411 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1412 int r = INPUT_OPTIONAL_UNSUPPORTED;
1413
1414 if (this && this->stream && data) {
1415 pthread_mutex_lock(&this->title_info_mutex);
1416 r = get_optional_data_impl(this, data, data_type);
1417 pthread_mutex_unlock(&this->title_info_mutex);
1418 }
1419
1420 return r;
1421 }
1422
bluray_plugin_dispose(input_plugin_t * this_gen)1423 static void bluray_plugin_dispose (input_plugin_t *this_gen)
1424 {
1425 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1426
1427 if (this->bdh) {
1428 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 2, 4)
1429 bd_register_argb_overlay_proc(this->bdh, NULL, NULL, NULL);
1430 #endif
1431 bd_register_overlay_proc(this->bdh, NULL, NULL);
1432 }
1433
1434 close_overlay(this, -1);
1435
1436 if (this->event_queue)
1437 xine_event_dispose_queue(this->event_queue);
1438
1439 pthread_mutex_lock(&this->title_info_mutex);
1440 if (this->title_info)
1441 bd_free_title_info(this->title_info);
1442 this->title_info = NULL;
1443 pthread_mutex_unlock(&this->title_info_mutex);
1444
1445 pthread_mutex_destroy(&this->title_info_mutex);
1446
1447 if (this->bdh)
1448 bd_close(this->bdh);
1449
1450 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 2, 4)
1451 osd_buf_destroy(&this->osd_buf);
1452 #endif
1453
1454 _x_freep (&this->mrl);
1455 _x_freep (&this->disc_root);
1456 _x_freep (&this->disc_name);
1457
1458 free (this);
1459 }
1460
parse_mrl(const char * mrl_in,char ** path,int * title,int * chapter)1461 static int parse_mrl(const char *mrl_in, char **path, int *title, int *chapter)
1462 {
1463 int skip = 0;
1464
1465 if (!strncasecmp(mrl_in, "bluray:", 7))
1466 skip = 7;
1467 else if (!strncasecmp(mrl_in, "bd:", 3))
1468 skip = 3;
1469 else
1470 return -1;
1471
1472 char *mrl = strdup(mrl_in + skip);
1473 if (!mrl)
1474 return 0;
1475
1476 /* title[.chapter] given ? parse and drop it */
1477 if (title && mrl[0] && mrl[strlen(mrl)-1] != '/') {
1478 char *end = strrchr(mrl, '/');
1479 int tail_len = 0;
1480 if (end && end[1]) {
1481 if (sscanf(end, "/%d.%d%n", title, chapter, &tail_len) < 1)
1482 *title = -1;
1483 else if (end[tail_len])
1484 *title = -1;
1485 else
1486 *end = 0;
1487 }
1488 lprintf(" -> title %d, chapter %d, mrl \'%s\'\n", *title, *chapter, mrl);
1489 }
1490
1491 if ((mrl[0] == 0) || !strcmp(mrl, "/") || !strcmp(mrl, "//") || !strcmp(mrl, "///")) {
1492
1493 /* default device */
1494 *path = NULL;
1495
1496 } else if (*mrl == '/') {
1497
1498 /* strip extra slashes */
1499 char *start = mrl;
1500 while (start[0] == '/' && start[1] == '/')
1501 start++;
1502
1503 *path = strdup(start);
1504
1505 _x_mrl_unescape(*path);
1506
1507 lprintf("non-defaut mount point \'%s\'\n", *path);
1508
1509 } else {
1510 lprintf("invalid mrl \'%s\'\n", mrl_in);
1511 free(mrl);
1512 return 0;
1513 }
1514
1515 free(mrl);
1516
1517 return 1;
1518 }
1519
get_disc_info(bluray_input_plugin_t * this)1520 static int get_disc_info(bluray_input_plugin_t *this)
1521 {
1522 const BLURAY_DISC_INFO *disc_info;
1523
1524 disc_info = bd_get_disc_info(this->bdh);
1525
1526 if (!disc_info) {
1527 LOGMSG("bd_get_disc_info() failed\n");
1528 return -1;
1529 }
1530
1531 if (!disc_info->bluray_detected) {
1532 LOGMSG("bd_get_disc_info(): BluRay not detected\n");
1533 this->nav_mode = 0;
1534 return 0;
1535 }
1536
1537 if (disc_info->aacs_detected && !disc_info->aacs_handled) {
1538 if (!disc_info->libaacs_detected)
1539 _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE,
1540 "Media stream scrambled/encrypted with AACS",
1541 "libaacs not installed", NULL);
1542 else
1543 _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE,
1544 "Media stream scrambled/encrypted with AACS", NULL);
1545 return -1;
1546 }
1547
1548 if (disc_info->bdplus_detected && !disc_info->bdplus_handled) {
1549 if (!disc_info->libbdplus_detected)
1550 _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE,
1551 "Media scrambled/encrypted with BD+",
1552 "libbdplus not installed.", NULL);
1553 else
1554 _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE,
1555 "Media stream scrambled/encrypted with BD+", NULL);
1556 return -1;
1557 }
1558
1559 if (this->nav_mode && !disc_info->first_play_supported) {
1560 _x_message (this->stream, XINE_MSG_GENERAL_WARNING,
1561 "Can't play disc using menus",
1562 "First Play title not supported", NULL);
1563 this->nav_mode = 0;
1564 }
1565
1566 if (this->nav_mode && disc_info->num_unsupported_titles > 0) {
1567 _x_message (this->stream, XINE_MSG_GENERAL_WARNING,
1568 "Unsupported titles found",
1569 "Some titles can't be played in navigation mode", NULL);
1570 }
1571
1572 if (this->nav_mode && disc_info->num_bdj_titles > 0) {
1573 if (!(this->stream->video_out->get_capabilities(this->stream->video_out) & VO_CAP_ARGB_LAYER_OVERLAY)) {
1574 _x_message (this->stream, XINE_MSG_GENERAL_WARNING,
1575 "BD-J titles found. Current video driver does not support ARGB graphics.",
1576 "Try another video driver (ex. --video opengl2) or play this disc without menus.",
1577 NULL);
1578 }
1579 }
1580
1581 this->num_titles = disc_info->num_hdmv_titles + disc_info->num_bdj_titles;
1582 this->disc_info = disc_info;
1583
1584 return 1;
1585 }
1586
get_disc_name(const char * path)1587 static char *get_disc_name(const char *path)
1588 {
1589 const char *name_start;
1590 char *file_name = NULL;
1591 int len;
1592
1593 name_start = path + strlen(path) - 1;
1594 /* skip trailing '/' */
1595 while (name_start > path && name_start[0] == '/')
1596 name_start--;
1597 /* find prev '/' */
1598 while (name_start > path && name_start[-1] != '/')
1599 name_start--;
1600
1601 file_name = strdup(name_start);
1602 len = strlen(file_name);
1603
1604 /* trim trailing '/' */
1605 while (len > 0 && file_name[len - 1] == '/')
1606 file_name[--len] = 0;
1607
1608 /* trim trailing ".iso" */
1609 if (len > 3 && !strcasecmp(file_name + len - 4, ".iso"))
1610 file_name[len - 4] = 0;
1611
1612 /* '_' --> ' ' */
1613 for (len = 0; file_name[len]; ++len)
1614 if (file_name[len] == '_')
1615 file_name[len] = ' ';
1616
1617 lprintf("disc name: %s\n", file_name);
1618 return file_name;
1619 }
1620
is_iso_image(const char * mrl)1621 static int is_iso_image(const char *mrl)
1622 {
1623 if (mrl) {
1624 const char *pos = strrchr(mrl, '.');
1625 return pos && !strcasecmp(pos + 1, "iso");
1626 }
1627 return 0;
1628 }
1629
bluray_plugin_open(input_plugin_t * this_gen)1630 static int bluray_plugin_open (input_plugin_t *this_gen)
1631 {
1632 bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
1633 int title = -1;
1634 int chapter = 0;
1635 int major, minor, micro;
1636
1637 lprintf("bluray_plugin_open '%s'\n",this->mrl);
1638
1639 /* validate and parse mrl */
1640 if (!parse_mrl(this->mrl, &this->disc_root, &title, &chapter))
1641 return -1;
1642
1643 if (!strncasecmp(this->mrl, "bd:", 3))
1644 this->nav_mode = 1;
1645
1646 if (!this->disc_root)
1647 this->disc_root = strdup(this->class->mountpoint);
1648
1649 bd_get_version(&major, &minor, µ);
1650 if (BLURAY_VERSION_CODE(major, minor, micro) < BLURAY_VERSION_CODE(0, 8, 0)) {
1651 if (is_iso_image(this->disc_root)) {
1652 _x_message (this->stream, XINE_MSG_GENERAL_WARNING,
1653 "Can't play BluRay .iso image. Update libbluray.",
1654 "", NULL);
1655 return -1;
1656 }
1657 }
1658
1659 /* open libbluray */
1660
1661 if (! (this->bdh = bd_open (this->disc_root, NULL))) {
1662 LOGMSG("bd_open(\'%s\') failed: %s\n", this->disc_root, strerror(errno));
1663 return -1;
1664 }
1665 lprintf("bd_open(\'%s\') OK\n", this->disc_root);
1666
1667 if (get_disc_info(this) < 0) {
1668 return -1;
1669 }
1670
1671 /* load title list */
1672
1673 if (!this->nav_mode) {
1674 this->num_title_idx = bd_get_titles(this->bdh, TITLES_RELEVANT, MIN_TITLE_LENGTH);
1675 LOGMSG("%d titles\n", this->num_title_idx);
1676
1677 if (this->num_title_idx < 1)
1678 return -1;
1679
1680 /* if title was not in mrl, guess the main title */
1681 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 5, 0)
1682 if (title < 0) {
1683 title = bd_get_main_title(this->bdh);
1684 LOGMSG("main title: %d\n", title);
1685 }
1686 #endif
1687 if (title < 0) {
1688 uint64_t duration = 0;
1689 int i, playlist = 99999;
1690 for (i = 0; i < this->num_title_idx; i++) {
1691 BLURAY_TITLE_INFO *info = bd_get_title_info(this->bdh, i, 0);
1692 if (info->duration > duration) {
1693 title = i;
1694 duration = info->duration;
1695 playlist = info->playlist;
1696 }
1697 bd_free_title_info(info);
1698 }
1699 LOGMSG("main title: %d (%05d.mpls)\n", title, playlist);
1700 }
1701
1702 } else {
1703 LOGMSG("%d titles\n", this->num_titles);
1704 }
1705
1706 /* update player settings */
1707
1708 bd_set_player_setting (this->bdh, BLURAY_PLAYER_SETTING_REGION_CODE, this->class->region);
1709 bd_set_player_setting (this->bdh, BLURAY_PLAYER_SETTING_PARENTAL, this->class->parental);
1710 bd_set_player_setting_str(this->bdh, BLURAY_PLAYER_SETTING_AUDIO_LANG, this->class->language);
1711 bd_set_player_setting_str(this->bdh, BLURAY_PLAYER_SETTING_PG_LANG, this->class->language);
1712 bd_set_player_setting_str(this->bdh, BLURAY_PLAYER_SETTING_MENU_LANG, this->class->language);
1713 bd_set_player_setting_str(this->bdh, BLURAY_PLAYER_SETTING_COUNTRY_CODE, this->class->country);
1714
1715 /* init event queue */
1716 bd_get_event(this->bdh, NULL);
1717
1718 /* get disc name */
1719
1720 this->meta_dl = bd_get_meta(this->bdh);
1721
1722 if (this->meta_dl && this->meta_dl->di_name && strlen(this->meta_dl->di_name) > 1) {
1723 this->disc_name = strdup(this->meta_dl->di_name);
1724 }
1725 else if (strcmp(this->disc_root, this->class->mountpoint)) {
1726 this->disc_name = get_disc_name(this->disc_root);
1727 }
1728
1729 /* register overlay (graphics) handler */
1730
1731 #if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 2, 4)
1732 if (this->stream->video_out->get_capabilities(this->stream->video_out) & VO_CAP_ARGB_LAYER_OVERLAY) {
1733 osd_buf_init(&this->osd_buf);
1734 bd_register_argb_overlay_proc(this->bdh, this, argb_overlay_proc, &this->osd_buf.buf);
1735 }
1736 #endif
1737
1738 bd_register_overlay_proc(this->bdh, this, overlay_proc);
1739
1740 /* open */
1741 this->current_title = -1;
1742 this->current_title_idx = -1;
1743
1744 if (this->nav_mode) {
1745 if (bd_play(this->bdh) <= 0) {
1746 LOGMSG("bd_play() failed\n");
1747 return -1;
1748 }
1749
1750 } else {
1751 if (open_title(this, title) <= 0 &&
1752 open_title(this, 0) <= 0)
1753 return -1;
1754 }
1755
1756 /* jump to chapter */
1757
1758 if (chapter > 0) {
1759 chapter = MAX(0, MIN((int)this->title_info->chapter_count, chapter) - 1);
1760 bd_seek_chapter(this->bdh, chapter);
1761 _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER, chapter + 1);
1762 }
1763
1764 return 1;
1765 }
1766
bluray_class_get_instance(input_class_t * cls_gen,xine_stream_t * stream,const char * mrl)1767 static input_plugin_t *bluray_class_get_instance (input_class_t *cls_gen, xine_stream_t *stream,
1768 const char *mrl)
1769 {
1770 bluray_input_plugin_t *this;
1771
1772 if (strncasecmp(mrl, "bluray:", 7) && strncasecmp(mrl, "bd:", 3))
1773 return NULL;
1774
1775 this = calloc(1, sizeof (bluray_input_plugin_t));
1776 if (!this)
1777 return NULL;
1778
1779 this->stream = stream;
1780 this->class = (bluray_input_class_t*)cls_gen;
1781 this->mrl = strdup(mrl);
1782 if (!this->mrl) {
1783 free(this);
1784 return NULL;
1785 }
1786
1787 this->input_plugin.open = bluray_plugin_open;
1788 this->input_plugin.get_capabilities = bluray_plugin_get_capabilities;
1789 this->input_plugin.read = bluray_plugin_read;
1790 this->input_plugin.read_block = bluray_plugin_read_block;
1791 this->input_plugin.seek = bluray_plugin_seek;
1792 this->input_plugin.seek_time = bluray_plugin_seek_time;
1793 this->input_plugin.get_current_pos = bluray_plugin_get_current_pos;
1794 this->input_plugin.get_current_time = bluray_plugin_get_current_time;
1795 this->input_plugin.get_length = bluray_plugin_get_length;
1796 this->input_plugin.get_blocksize = bluray_plugin_get_blocksize;
1797 this->input_plugin.get_mrl = bluray_plugin_get_mrl;
1798 this->input_plugin.get_optional_data = bluray_plugin_get_optional_data;
1799 this->input_plugin.dispose = bluray_plugin_dispose;
1800 this->input_plugin.input_class = cls_gen;
1801
1802 this->event_queue = xine_event_new_queue (this->stream);
1803
1804 pthread_mutex_init(&this->title_info_mutex, NULL);
1805
1806 this->pg_stream = -1;
1807
1808 return &this->input_plugin;
1809 }
1810
1811 /*
1812 * plugin class
1813 */
1814
mountpoint_change_cb(void * data,xine_cfg_entry_t * cfg)1815 static void mountpoint_change_cb(void *data, xine_cfg_entry_t *cfg)
1816 {
1817 bluray_input_class_t *class = (bluray_input_class_t *) data;
1818
1819 class->mountpoint = cfg->str_value;
1820 }
1821
device_change_cb(void * data,xine_cfg_entry_t * cfg)1822 static void device_change_cb(void *data, xine_cfg_entry_t *cfg)
1823 {
1824 bluray_input_class_t *class = (bluray_input_class_t *) data;
1825
1826 class->device = cfg->str_value;
1827 }
1828
language_change_cb(void * data,xine_cfg_entry_t * cfg)1829 static void language_change_cb(void *data, xine_cfg_entry_t *cfg)
1830 {
1831 bluray_input_class_t *class = (bluray_input_class_t *) data;
1832
1833 class->language = cfg->str_value;
1834 }
1835
country_change_cb(void * data,xine_cfg_entry_t * cfg)1836 static void country_change_cb(void *data, xine_cfg_entry_t *cfg)
1837 {
1838 bluray_input_class_t *class = (bluray_input_class_t *) data;
1839
1840 class->country = cfg->str_value;
1841 }
1842
region_change_cb(void * data,xine_cfg_entry_t * cfg)1843 static void region_change_cb(void *data, xine_cfg_entry_t *cfg)
1844 {
1845 bluray_input_class_t *class = (bluray_input_class_t *) data;
1846
1847 class->region = cfg->num_value;
1848 }
1849
parental_change_cb(void * data,xine_cfg_entry_t * cfg)1850 static void parental_change_cb(void *data, xine_cfg_entry_t *cfg)
1851 {
1852 bluray_input_class_t *class = (bluray_input_class_t *) data;
1853
1854 class->parental = cfg->num_value;
1855 }
1856
skip_mode_change_cb(void * data,xine_cfg_entry_t * cfg)1857 static void skip_mode_change_cb (void *data, xine_cfg_entry_t *cfg) {
1858 bluray_input_class_t *class = (bluray_input_class_t *) data;
1859 class->skip_mode = cfg->num_value;
1860 }
1861
bluray_class_get_autoplay_list(input_class_t * this_gen,int * num_files)1862 static const char * const *bluray_class_get_autoplay_list (input_class_t *this_gen, int *num_files)
1863 {
1864 static const char * const autoplay_list[] = { "bluray:/", NULL };
1865
1866 (void)this_gen;
1867 *num_files = 1;
1868
1869 return autoplay_list;
1870 }
1871
bluray_class_get_dir(input_class_t * this_gen,const char * filename,int * nFiles)1872 static xine_mrl_t **bluray_class_get_dir(input_class_t *this_gen, const char *filename, int *nFiles)
1873 {
1874 bluray_input_class_t *this = (bluray_input_class_t*) this_gen;
1875 char *path = NULL;
1876 int title = -1, chapter = -1, i, num_pl;
1877 BLURAY *bdh;
1878
1879 lprintf("bluray_class_get_dir(%s)\n", filename);
1880
1881 _x_input_free_mrls(&this->xine_playlist);
1882 *nFiles = 0;
1883
1884 if (filename)
1885 parse_mrl(filename, &path, &title, &chapter);
1886
1887 bdh = bd_open(path ? path : this->mountpoint, NULL);
1888 if (!bdh)
1889 goto out;
1890
1891 num_pl = bd_get_titles(bdh, TITLES_RELEVANT, MIN_TITLE_LENGTH);
1892 if (num_pl < 1)
1893 goto out;
1894
1895 this->xine_playlist = _x_input_alloc_mrls(num_pl);
1896 if (!this->xine_playlist)
1897 goto out;
1898
1899 for (i = 0; i < num_pl; i++) {
1900 this->xine_playlist[i]->origin = _x_asprintf("bluray:/%s", path ? path : "");
1901 this->xine_playlist[i]->mrl = _x_asprintf("bluray:/%s/%d", path ? path : "", i);
1902 this->xine_playlist[i]->type = mrl_dvd;
1903 }
1904
1905 *nFiles = num_pl;
1906
1907 out:
1908 if (bdh)
1909 bd_close(bdh);
1910 free(path);
1911 return this->xine_playlist;
1912 }
1913
bluray_class_eject_media(input_class_t * this_gen)1914 static int bluray_class_eject_media (input_class_t *this_gen)
1915 {
1916 bluray_input_class_t *this = (bluray_input_class_t*) this_gen;
1917
1918 return media_eject_media (this->xine, this->device);
1919 }
1920
bluray_class_dispose(input_class_t * this_gen)1921 static void bluray_class_dispose (input_class_t *this_gen)
1922 {
1923 bluray_input_class_t *this = (bluray_input_class_t *) this_gen;
1924 config_values_t *config = this->xine->config;
1925
1926 _x_input_free_mrls(&this->xine_playlist);
1927
1928 config->unregister_callbacks (config, NULL, NULL, this, sizeof (*this));
1929
1930 free (this);
1931 }
1932
bluray_init_plugin(xine_t * xine,const void * data)1933 static void *bluray_init_plugin (xine_t *xine, const void *data)
1934 {
1935 static const char * const skip_modes[] = {"skip chapter", "skip title", NULL};
1936
1937 config_values_t *config = xine->config;
1938 bluray_input_class_t *this = (bluray_input_class_t *) calloc(1, sizeof (bluray_input_class_t));
1939 if (!this)
1940 return NULL;
1941
1942 (void)data;
1943 this->xine = xine;
1944
1945 this->input_class.get_instance = bluray_class_get_instance;
1946 this->input_class.get_dir = bluray_class_get_dir;
1947 this->input_class.get_autoplay_list = bluray_class_get_autoplay_list;
1948 this->input_class.dispose = bluray_class_dispose;
1949 this->input_class.eject_media = bluray_class_eject_media;
1950
1951 this->input_class.identifier = "bluray";
1952 this->input_class.description = _("BluRay input plugin");
1953
1954 this->mountpoint = config->register_filename (config, "media.bluray.mountpoint",
1955 BLURAY_MNT_PATH, XINE_CONFIG_STRING_IS_DIRECTORY_NAME,
1956 _("BluRay mount point"),
1957 _("Default mount location for BluRay discs."),
1958 0, mountpoint_change_cb, (void *) this);
1959
1960 this->device = config->register_filename (config, "media.bluray.device",
1961 BLURAY_PATH, XINE_CONFIG_STRING_IS_DEVICE_NAME,
1962 _("device used for BluRay playback"),
1963 _("The path to the device which you intend to use for playing BluRay discs."),
1964 0, device_change_cb, (void *) this);
1965
1966 /* Player settings */
1967 this->language = config->register_string (config, "media.bluray.language",
1968 "eng",
1969 _("default language for BluRay playback"),
1970 _("xine tries to use this language as a default for BluRay playback. "
1971 "As far as the BluRay supports it, menus and audio tracks will be presented in this language.\n"
1972 "The value must be a three characterISO639-2 language code."),
1973 0, language_change_cb, this);
1974
1975 this->country = config->register_string (config, "media.bluray.country",
1976 "en",
1977 _("BluRay player country code"),
1978 _("The value must be a two character ISO3166-1 country code."),
1979 0, country_change_cb, this);
1980
1981 this->region = config->register_num (config, "media.bluray.region",
1982 7,
1983 _("BluRay player region code (1=A, 2=B, 4=C)"),
1984 _("This only needs to be changed if your BluRay jumps to a screen "
1985 "complaining about a wrong region code. It has nothing to do with "
1986 "the region code set in BluRay drives, this is purely software."),
1987 0, region_change_cb, this);
1988
1989 this->parental = config->register_num (config, "media.bluray.parental",
1990 99,
1991 _("parental control age limit (1-99)"),
1992 _("Prevents playback of BluRay titles where parental control age limit "
1993 "is higher than this limit"),
1994 0, parental_change_cb, this);
1995
1996 this->skip_mode = config->register_enum (config, "media.bluray.skip_behaviour",
1997 0,
1998 (char **)skip_modes,
1999 _("unit for the skip action"),
2000 _("You can configure the behaviour when issuing a skip command (using the skip "
2001 "buttons for example)."),
2002 20, skip_mode_change_cb, this);
2003
2004 return this;
2005 }
2006
bd_class_get_autoplay_list(input_class_t * this_gen,int * num_files)2007 static const char * const *bd_class_get_autoplay_list (input_class_t *this_gen, int *num_files)
2008 {
2009 static const char * const autoplay_list[] = { "bd:/", NULL };
2010
2011 (void)this_gen;
2012 *num_files = 1;
2013
2014 return autoplay_list;
2015 }
2016
bd_init_plugin(xine_t * xine,const void * data)2017 static void *bd_init_plugin (xine_t *xine, const void *data)
2018 {
2019 bluray_input_class_t *this = bluray_init_plugin(xine, data);
2020
2021 if (this) {
2022 this->input_class.identifier = "bluray";
2023 this->input_class.description = _("BluRay input plugin (using menus)");
2024
2025 this->input_class.get_dir = NULL;
2026 this->input_class.get_autoplay_list = bd_class_get_autoplay_list;
2027 }
2028
2029 return this;
2030 }
2031
2032 /*
2033 * exported plugin catalog entry
2034 */
2035
2036 const plugin_info_t xine_plugin_info[] EXPORTED = {
2037 /* type, API, "name", version, special_info, init_function */
2038 { PLUGIN_INPUT, 18, "BLURAY", XINE_VERSION_CODE, NULL, bluray_init_plugin },
2039 { PLUGIN_INPUT, 18, "BD", XINE_VERSION_CODE, NULL, bd_init_plugin },
2040 { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
2041 };
2042