1 /*****************************************************************************
2 * bluray.c: Blu-ray disc support plugin
3 *****************************************************************************
4 * Copyright (C) 2010 VideoLAN, VLC authors and libbluray AUTHORS
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
25 #ifdef HAVE_SYS_STAT_H
26 # include <sys/stat.h>
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_access.h>
35 #include <vlc_messages.h>
36 #include <vlc_input.h>
37 #include <vlc_dialog.h>
39 #include <libbluray/bluray.h>
41 /*****************************************************************************
43 *****************************************************************************/
44 #define CACHING_TEXT N_("Caching value in ms")
45 #define CACHING_LONGTEXT N_( "Caching value for BDs. This "\
46 "value should be set in milliseconds." )
49 static int blurayOpen ( vlc_object_t * );
50 static void blurayClose( vlc_object_t * );
53 set_shortname( N_("BluRay") )
54 set_description( N_("Blu-Ray Disc support (libbluray)") )
56 set_category( CAT_INPUT )
57 set_subcategory( SUBCAT_INPUT_ACCESS )
58 set_capability( "access", 60 )
60 add_integer( "bluray-caching", 1000,
61 CACHING_TEXT, CACHING_LONGTEXT, true )
63 add_shortcut( "bluray" )
64 add_shortcut( "file" )
66 set_callbacks( blurayOpen, blurayClose )
69 /*****************************************************************************
71 *****************************************************************************/
79 unsigned int i_longest_title;
80 input_title_t **pp_title;
85 static ssize_t blurayRead (access_t *, uint8_t *, size_t);
86 static int bluraySeek (access_t *, uint64_t);
87 static int blurayControl(access_t *, int, va_list);
88 static int blurayInitTitles(access_t *p_access );
89 static int bluraySetTitle(access_t *p_access, int i_title);
91 /*****************************************************************************
92 * blurayOpen: module init function
93 *****************************************************************************/
94 static int blurayOpen( vlc_object_t *object )
96 access_t *p_access = (access_t*)object;
101 char bd_path[PATH_MAX];
103 if( strcmp( p_access->psz_access, "bluray" ) ) {
104 // TODO BDMV support, once we figure out what to do in libbluray
108 /* init access fields */
109 access_InitFields(p_access);
111 /* register callback function for communication */
112 ACCESS_SET_CALLBACKS(blurayRead, NULL, blurayControl, bluraySeek);
114 p_access->p_sys = p_sys = malloc(sizeof(access_sys_t));
115 if (unlikely(!p_sys)) {
119 TAB_INIT( p_sys->i_title, p_sys->pp_title );
121 /* store current bd_path */
122 strncpy(bd_path, p_access->psz_location, sizeof(bd_path));
123 bd_path[PATH_MAX - 1] = '\0';
125 p_sys->bluray = bd_open(bd_path, NULL);
126 if ( !p_sys->bluray ) {
131 /* Warning the user about AACS/BD+ */
132 const BLURAY_DISC_INFO *disc_info = bd_get_disc_info(p_sys->bluray);
133 msg_Dbg (p_access, "First play: %i, Top menu: %i\n"
134 "HDMV Titles: %i, BDJ Titles: %i, Other: %i",
135 disc_info->first_play_supported, disc_info->top_menu_supported,
136 disc_info->num_hdmv_titles, disc_info->num_bdj_titles,
137 disc_info->num_unsupported_titles);
140 if (disc_info->aacs_detected) {
141 if (!disc_info->libaacs_detected) {
142 dialog_Fatal (p_access, _("Blu-Ray error"),
143 _("This Blu-Ray Disc needs a library for AACS decoding, "
144 "and your system does not have it."));
148 if (!disc_info->aacs_handled) {
149 dialog_Fatal (p_access, _("Blu-Ray error"),
150 _("Your system AACS decoding library does not work. "
158 if (disc_info->bdplus_detected) {
159 if (!disc_info->libbdplus_detected) {
160 dialog_Fatal (p_access, _("Blu-Ray error"),
161 _("This Blu-Ray Disc needs a library for BD+ decoding, "
162 "and your system does not have it."));
166 if (!disc_info->bdplus_handled) {
167 dialog_Fatal (p_access, _("Blu-Ray error"),
168 _("Your system BD+ decoding library does not work. "
169 "Missing configuration?"));
175 /* Get titles and chapters */
176 if (blurayInitTitles(p_access) != VLC_SUCCESS) {
181 /* get title request */
182 if ( (pos_title = strrchr(bd_path, ':')) ) {
183 /* found character ':' for title information */
184 *(pos_title++) = '\0';
185 i_title = atoi(pos_title);
188 /* set start title number */
189 if ( bluraySetTitle(p_access, i_title) != VLC_SUCCESS ) {
190 msg_Err( p_access, "Could not set the title %d", i_title );
195 p_sys->i_bd_delay = var_InheritInteger(p_access, "bluray-caching");
201 /*****************************************************************************
202 * blurayClose: module destroy function
203 *****************************************************************************/
204 static void blurayClose( vlc_object_t *object )
206 access_t *p_access = (access_t*)object;
207 access_sys_t *p_sys = p_access->p_sys;
210 for (unsigned int i = 0; i < p_sys->i_title; i++)
211 vlc_input_title_Delete(p_sys->pp_title[i]);
212 TAB_CLEAN( p_sys->i_title, p_sys->pp_title );
214 /* bd_close( NULL ) can crash */
215 assert(p_sys->bluray);
216 bd_close(p_sys->bluray);
220 static int blurayInitTitles(access_t *p_access )
222 access_sys_t *p_sys = p_access->p_sys;
224 /* get and set the titles */
225 unsigned i_title = bd_get_titles(p_sys->bluray, TITLES_RELEVANT);
226 int64_t duration = 0;
228 for (unsigned int i = 0; i < i_title; i++) {
229 input_title_t *t = vlc_input_title_New();
233 BLURAY_TITLE_INFO *title_info = bd_get_title_info(p_sys->bluray,i);
236 t->i_length = title_info->duration * CLOCK_FREQ / INT64_C(90000);
238 if (t->i_length > duration) {
239 duration = t->i_length;
240 p_sys->i_longest_title = i;
243 for ( unsigned int j = 0; j < title_info->chapter_count; j++) {
244 seekpoint_t *s = vlc_seekpoint_New();
247 s->i_time_offset = title_info->chapters[j].offset;
249 TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
251 TAB_APPEND( p_sys->i_title, p_sys->pp_title, t );
252 bd_free_title_info(title_info);
257 /*****************************************************************************
258 * bluraySetTitle: select new BD title
259 *****************************************************************************/
260 static int bluraySetTitle(access_t *p_access, int i_title)
262 access_sys_t *p_sys = p_access->p_sys;
264 /* Looking for the main title, ie the longest duration */
266 i_title = p_sys->i_longest_title;
268 msg_Dbg( p_access, "Selecting Title %i", i_title);
270 /* Select Blu-Ray title */
271 if ( bd_select_title(p_access->p_sys->bluray, i_title) == 0 ) {
272 msg_Err( p_access, "cannot select bd title '%d'", p_access->info.i_title);
276 /* read title length and init some values */
277 p_access->info.i_title = i_title;
278 p_access->info.i_size = bd_get_title_size(p_sys->bluray);
279 p_access->info.i_pos = 0;
280 p_access->info.b_eof = false;
281 p_access->info.i_seekpoint = 0;
282 p_access->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
288 /*****************************************************************************
289 * blurayControl: handle the controls
290 *****************************************************************************/
291 static int blurayControl(access_t *p_access, int query, va_list args)
293 access_sys_t *p_sys = p_access->p_sys;
298 case ACCESS_CAN_SEEK:
299 case ACCESS_CAN_FASTSEEK:
300 case ACCESS_CAN_PAUSE:
301 case ACCESS_CAN_CONTROL_PACE:
302 pb_bool = (bool*)va_arg( args, bool * );
306 case ACCESS_GET_PTS_DELAY:
307 pi_64 = (int64_t*)va_arg( args, int64_t * );
308 *pi_64 = p_sys->i_bd_delay;
311 case ACCESS_SET_PAUSE_STATE:
315 case ACCESS_SET_TITLE:
317 int i_title = (int)va_arg( args, int );
318 if( bluraySetTitle( p_access, i_title ) != VLC_SUCCESS )
322 case ACCESS_SET_SEEKPOINT:
324 int i_chapter = (int)va_arg( args, int );
325 bd_seek_chapter( p_sys->bluray, i_chapter );
326 p_access->info.i_update = INPUT_UPDATE_SEEKPOINT;
330 case ACCESS_GET_TITLE_INFO:
332 input_title_t ***ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
333 int *pi_int = (int*)va_arg( args, int* );
334 int *pi_title_offset = (int*)va_arg( args, int* );
335 int *pi_chapter_offset = (int*)va_arg( args, int* );
338 *pi_title_offset = 0;
339 *pi_chapter_offset = 0;
341 /* Duplicate local title infos */
342 *pi_int = p_sys->i_title;
343 *ppp_title = calloc( p_sys->i_title, sizeof(input_title_t **) );
344 for( unsigned int i = 0; i < p_sys->i_title; i++ )
345 (*ppp_title)[i] = vlc_input_title_Duplicate( p_sys->pp_title[i]);
349 case ACCESS_SET_PRIVATE_ID_STATE:
350 case ACCESS_GET_CONTENT_TYPE:
351 case ACCESS_GET_META:
355 msg_Warn( p_access, "unimplemented query (%d) in control", query );
363 /*****************************************************************************
364 * bluraySeek: seek to the given position
365 *****************************************************************************/
366 static int bluraySeek(access_t *p_access, uint64_t position)
368 access_sys_t *p_sys = p_access->p_sys;
370 p_access->info.i_pos = bd_seek(p_sys->bluray, position);
371 p_access->info.b_eof = false;
377 /*****************************************************************************
378 * blurayRead: read BD data into buffer
379 *****************************************************************************/
380 static ssize_t blurayRead(access_t *p_access, uint8_t *data, size_t size)
382 access_sys_t *p_sys = p_access->p_sys;
385 if (p_access->info.b_eof) {
389 /* read data into buffer with given length */
390 nread = bd_read(p_sys->bluray, data, size);
393 p_access->info.b_eof = true;
395 else if( nread > 0 ) {
396 p_access->info.i_pos += nread;