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, &micro);
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