]> git.sesse.net Git - vlc/blob - modules/access/bluray.c
Blu-Ray: correctly update the navigation menu for 'titles'
[vlc] / modules / access / bluray.c
1 /*****************************************************************************
2  * bluray.c: Blu-ray disc support plugin
3  *****************************************************************************
4  * Copyright (C) 2010 VideoLAN, VLC authors and libbluray AUTHORS
5  *
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.
10  *
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.
15  *
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  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #ifdef HAVE_SYS_STAT_H
26 #   include <sys/stat.h>
27 #endif
28
29 #include <assert.h>
30 #include <limits.h>
31
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
38 #include <libbluray/bluray.h>
39
40 /*****************************************************************************
41  * Module descriptor
42  *****************************************************************************/
43 #define CACHING_TEXT N_("Caching value in ms")
44 #define CACHING_LONGTEXT N_( "Caching value for BDs. This "\
45                              "value should be set in milliseconds." )
46
47 /* Callbacks */
48 static int  blurayOpen ( vlc_object_t * );
49 static void blurayClose( vlc_object_t * );
50
51 vlc_module_begin ()
52     set_shortname( N_("BluRay") )
53     set_description( N_("Blu-Ray Disc support (libbluray)") )
54
55     set_category( CAT_INPUT )
56     set_subcategory( SUBCAT_INPUT_ACCESS )
57     set_capability( "access", 60 )
58
59     add_integer( "bluray-caching", 1000,
60         CACHING_TEXT, CACHING_LONGTEXT, true )
61
62     add_shortcut( "bluray" )
63     add_shortcut( "file" )
64
65     set_callbacks( blurayOpen, blurayClose )
66 vlc_module_end ()
67
68 /*****************************************************************************
69  * Local prototypes
70  *****************************************************************************/
71
72 struct access_sys_t
73 {
74     BLURAY *bluray; /* */
75
76     /* Titles */
77     unsigned int i_title;
78     unsigned int i_longest_title;
79     input_title_t   **pp_title;
80
81     int i_bd_delay;
82 };
83
84 static ssize_t blurayRead   (access_t *, uint8_t *, size_t);
85 static int     bluraySeek   (access_t *, uint64_t);
86 static int     blurayControl(access_t *, int, va_list);
87 static int     blurayInitTitles(access_t *p_access );
88 static int     bluraySetTitle(access_t *p_access, int i_title);
89
90 /*****************************************************************************
91  * blurayOpen: module init function
92  *****************************************************************************/
93 static int blurayOpen( vlc_object_t *object )
94 {
95     access_t *p_access = (access_t*)object;
96
97     access_sys_t *p_sys;
98     char *pos_title;
99     int i_title = -1;
100     char bd_path[PATH_MAX];
101
102     if( strcmp( p_access->psz_access, "bluray" ) ) {
103         // TODO BDMV support, once we figure out what to do in libbluray
104         return VLC_EGENERIC;
105     }
106
107     /* init access fields */
108     access_InitFields(p_access);
109
110     /* register callback function for communication */
111     ACCESS_SET_CALLBACKS(blurayRead, NULL, blurayControl, bluraySeek);
112
113     p_access->p_sys = p_sys = malloc(sizeof(access_sys_t));
114     if (unlikely(!p_sys)) {
115         return VLC_ENOMEM;
116     }
117
118     TAB_INIT( p_sys->i_title, p_sys->pp_title );
119
120     /* store current bd_path */
121     strncpy(bd_path, p_access->psz_location, sizeof(bd_path));
122     bd_path[PATH_MAX - 1] = '\0';
123
124     p_sys->bluray = bd_open(bd_path, NULL);
125     if ( !p_sys->bluray ) {
126         free(p_sys);
127         return VLC_EGENERIC;
128     }
129
130     if (blurayInitTitles(p_access) != VLC_SUCCESS) {
131         blurayClose(object);
132         return VLC_EGENERIC;
133     }
134
135     /* get title request */
136     if ( (pos_title = strrchr(bd_path, ':')) ) {
137         /* found character ':' for title information */
138         *(pos_title++) = '\0';
139         i_title = atoi(pos_title);
140     }
141
142     /* set start title number */
143     if ( bluraySetTitle(p_access, i_title) != VLC_SUCCESS ) {
144         msg_Err( p_access, "Could not set the title %d", i_title );
145         blurayClose(object);
146         return VLC_EGENERIC;
147     }
148
149     p_sys->i_bd_delay = var_InheritInteger(p_access, "bluray-caching");
150
151     return VLC_SUCCESS;
152 }
153
154
155 /*****************************************************************************
156  * blurayClose: module destroy function
157  *****************************************************************************/
158 static void blurayClose( vlc_object_t *object )
159 {
160     access_t *p_access = (access_t*)object;
161     access_sys_t *p_sys = p_access->p_sys;
162
163     /* Titles */
164     for (unsigned int i = 0; i < p_sys->i_title; i++)
165         vlc_input_title_Delete(p_sys->pp_title[i]);
166     TAB_CLEAN( p_sys->i_title, p_sys->pp_title );
167
168     /* bd_close( NULL ) can crash */
169     assert(p_sys->bluray);
170     bd_close(p_sys->bluray);
171     free(p_sys);
172 }
173
174 static int blurayInitTitles(access_t *p_access )
175 {
176     access_sys_t *p_sys = p_access->p_sys;
177
178     /* get and set the titles */
179     unsigned i_title = bd_get_titles(p_sys->bluray, TITLES_RELEVANT);
180     int64_t duration = 0;
181
182     for (unsigned int i = 0; i < i_title; i++) {
183         input_title_t *t = vlc_input_title_New();
184         if (!t)
185             break;
186
187         BLURAY_TITLE_INFO *title_info = bd_get_title_info(p_sys->bluray,i);
188         if (!title_info)
189             break;
190         t->i_length = title_info->duration * CLOCK_FREQ / INT64_C(90000);
191
192         if (t->i_length > duration) {
193             duration = t->i_length;
194             p_sys->i_longest_title = i;
195         }
196
197         for ( unsigned int j = 0; j < title_info->chapter_count; j++) {
198             seekpoint_t *s = vlc_seekpoint_New();
199             if( !s )
200                 break;
201             s->i_time_offset = title_info->chapters[j].offset;
202
203             TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
204         }
205         TAB_APPEND( p_sys->i_title, p_sys->pp_title, t );
206         bd_free_title_info(title_info);
207     }
208     return VLC_SUCCESS;
209 }
210
211 /*****************************************************************************
212  * bluraySetTitle: select new BD title
213  *****************************************************************************/
214 static int bluraySetTitle(access_t *p_access, int i_title)
215 {
216     access_sys_t *p_sys = p_access->p_sys;
217
218     /* Looking for the main title, ie the longest duration */
219     if (i_title == -1)
220         i_title = p_sys->i_longest_title;
221
222     msg_Dbg( p_access, "Selecting Title %i", i_title);
223
224     /* Select Blu-Ray title */
225     if ( bd_select_title(p_access->p_sys->bluray, i_title) == 0 ) {
226         msg_Err( p_access, "cannot select bd title '%d'", p_access->info.i_title);
227         return VLC_EGENERIC;
228     }
229
230     /* read title length and init some values */
231     p_access->info.i_title = i_title;
232     p_access->info.i_size  = bd_get_title_size(p_sys->bluray);
233     p_access->info.i_pos   = 0;
234     p_access->info.b_eof   = false;
235     p_access->info.i_seekpoint = 0;
236     p_access->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
237
238     return VLC_SUCCESS;
239 }
240
241
242 /*****************************************************************************
243  * blurayControl: handle the controls
244  *****************************************************************************/
245 static int blurayControl(access_t *p_access, int query, va_list args)
246 {
247     access_sys_t *p_sys = p_access->p_sys;
248     bool     *pb_bool;
249     int64_t  *pi_64;
250
251     switch (query) {
252         case ACCESS_CAN_SEEK:
253         case ACCESS_CAN_FASTSEEK:
254         case ACCESS_CAN_PAUSE:
255         case ACCESS_CAN_CONTROL_PACE:
256              pb_bool = (bool*)va_arg( args, bool * );
257              *pb_bool = true;
258              break;
259
260         case ACCESS_GET_PTS_DELAY:
261             pi_64 = (int64_t*)va_arg( args, int64_t * );
262             *pi_64 = p_sys->i_bd_delay;
263             break;
264
265         case ACCESS_SET_PAUSE_STATE:
266             /* Nothing to do */
267             break;
268
269         case ACCESS_SET_TITLE:
270         {
271             int i_title = (int)va_arg( args, int );
272             if( bluraySetTitle( p_access, i_title ) != VLC_SUCCESS )
273                 return VLC_EGENERIC;
274             break;
275         }
276         case ACCESS_SET_SEEKPOINT:
277         {
278             int i_chapter = (int)va_arg( args, int );
279             bd_seek_chapter( p_sys->bluray, i_chapter );
280             p_access->info.i_update = INPUT_UPDATE_SEEKPOINT;
281             break;
282         }
283
284         case ACCESS_GET_TITLE_INFO:
285         {
286             input_title_t ***ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
287             int *pi_int             = (int*)va_arg( args, int* );
288             int *pi_title_offset    = (int*)va_arg( args, int* );
289             int *pi_chapter_offset  = (int*)va_arg( args, int* );
290
291             /* */
292             *pi_title_offset   = 0;
293             *pi_chapter_offset = 0;
294
295             /* Duplicate local title infos */
296             *pi_int = p_sys->i_title;
297             *ppp_title = calloc( p_sys->i_title, sizeof(input_title_t **) );
298             for( unsigned int i = 0; i < p_sys->i_title; i++ )
299                 (*ppp_title)[i] = vlc_input_title_Duplicate( p_sys->pp_title[i]);
300
301             return VLC_SUCCESS;
302         }
303         case ACCESS_SET_PRIVATE_ID_STATE:
304         case ACCESS_GET_CONTENT_TYPE:
305         case ACCESS_GET_META:
306             return VLC_EGENERIC;
307
308         default:
309             msg_Warn( p_access, "unimplemented query (%d) in control", query );
310             return VLC_EGENERIC;
311     }
312
313     return VLC_SUCCESS;
314 }
315
316
317 /*****************************************************************************
318  * bluraySeek: seek to the given position
319  *****************************************************************************/
320 static int bluraySeek(access_t *p_access, uint64_t position)
321 {
322     access_sys_t *p_sys = p_access->p_sys;
323
324     p_access->info.i_pos = bd_seek(p_sys->bluray, position);
325     p_access->info.b_eof = false;
326
327     return VLC_SUCCESS;
328 }
329
330
331 /*****************************************************************************
332  * blurayRead: read BD data into buffer
333  *****************************************************************************/
334 static ssize_t blurayRead(access_t *p_access, uint8_t *data, size_t size)
335 {
336     access_sys_t *p_sys = p_access->p_sys;
337     int nread;
338
339     if (p_access->info.b_eof) {
340         return 0;
341     }
342
343     /* read data into buffer with given length */
344     nread = bd_read(p_sys->bluray, data, size);
345
346     if( nread == 0 ) {
347         p_access->info.b_eof = true;
348     }
349     else if( nread > 0 ) {
350         p_access->info.i_pos += nread;
351     }
352
353     return nread;
354 }
355