1 /*****************************************************************************
2 * vcd.c : VCD input module for vlc
3 *****************************************************************************
4 * Copyright © 2000-2011 VLC authors and VideoLAN
5 * $Id: abdeaa624628b775c289a8f4712b4801ed390884 $
6 *
7 * Author: Johan Bilien <jobi@via.ecp.fr>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
23
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_input.h>
35 #include <vlc_access.h>
36 #include <vlc_charset.h>
37
38 #include "cdrom.h"
39
40 /*****************************************************************************
41 * Module descriptior
42 *****************************************************************************/
43 static int Open ( vlc_object_t * );
44 static void Close( vlc_object_t * );
45
46 vlc_module_begin ()
47 set_shortname( N_("VCD"))
48 set_description( N_("VCD input") )
49 set_capability( "access", 60 )
50 set_callbacks( Open, Close )
51 set_category( CAT_INPUT )
52 set_subcategory( SUBCAT_INPUT_ACCESS )
53
54 add_usage_hint( N_("[vcd:][device][#[title][,[chapter]]]") )
55 add_shortcut( "vcd", "svcd" )
56 vlc_module_end ()
57
58 /*****************************************************************************
59 * Local prototypes
60 *****************************************************************************/
61
62 /* how many blocks VCDRead will read in each loop */
63 #define VCD_BLOCKS_ONCE 20
64 #define VCD_DATA_ONCE (VCD_BLOCKS_ONCE * VCD_DATA_SIZE)
65
66 struct access_sys_t
67 {
68 vcddev_t *vcddev; /* vcd device descriptor */
69 uint64_t offset;
70
71 /* Title infos */
72 int i_titles;
73 struct
74 {
75 uint64_t *seekpoints;
76 size_t count;
77 } titles[99]; /* No more that 99 track in a vcd ? */
78 int i_current_title;
79 unsigned i_current_seekpoint;
80
81 int i_sector; /* Current Sector */
82 int *p_sectors; /* Track sectors */
83 };
84
85 static block_t *Block( stream_t *, bool * );
86 static int Seek( stream_t *, uint64_t );
87 static int Control( stream_t *, int, va_list );
88 static int EntryPoints( stream_t * );
89
90 /*****************************************************************************
91 * VCDOpen: open vcd
92 *****************************************************************************/
Open(vlc_object_t * p_this)93 static int Open( vlc_object_t *p_this )
94 {
95 stream_t *p_access = (stream_t *)p_this;
96 access_sys_t *p_sys;
97 if( p_access->psz_filepath == NULL )
98 return VLC_EGENERIC;
99
100 char *psz_dup = ToLocaleDup( p_access->psz_filepath );
101 char *psz;
102 int i_title = 0;
103 int i_chapter = 0;
104 vcddev_t *vcddev;
105
106 /* Command line: vcd://[dev_path][#title[,chapter]] */
107 if( ( psz = strchr( psz_dup, '#' ) ) )
108 {
109 *psz++ = '\0';
110
111 i_title = strtol( psz, &psz, 0 );
112 if( *psz )
113 i_chapter = strtol( psz+1, &psz, 0 );
114 }
115
116 if( *psz_dup == '\0' )
117 {
118 free( psz_dup );
119
120 /* Only when selected */
121 if( strcmp( p_access->psz_name, "vcd" ) &&
122 strcmp( p_access->psz_name, "svcd" ) )
123 return VLC_EGENERIC;
124
125 psz_dup = var_CreateGetString( p_access, "vcd" );
126 if( *psz_dup == '\0' )
127 {
128 free( psz_dup );
129 return VLC_EGENERIC;
130 }
131 }
132
133 #if defined( _WIN32 ) || defined( __OS2__ )
134 if( psz_dup[0] && psz_dup[1] == ':' &&
135 psz_dup[2] == '\\' && psz_dup[3] == '\0' ) psz_dup[2] = '\0';
136 #endif
137
138 /* Open VCD */
139 vcddev = ioctl_Open( p_this, psz_dup );
140 free( psz_dup );
141 if( !vcddev )
142 return VLC_EGENERIC;
143
144 /* Set up p_access */
145 p_access->p_sys = p_sys = calloc( 1, sizeof( access_sys_t ) );
146 if( unlikely(!p_sys ))
147 goto error;
148 p_sys->vcddev = vcddev;
149 p_sys->offset = 0;
150
151 for( size_t i = 0; i < ARRAY_SIZE(p_sys->titles); i++ )
152 p_sys->titles[i].seekpoints = NULL;
153
154 /* We read the Table Of Content information */
155 p_sys->i_titles = ioctl_GetTracksMap( VLC_OBJECT(p_access),
156 p_sys->vcddev, &p_sys->p_sectors );
157 if( p_sys->i_titles < 0 )
158 {
159 msg_Err( p_access, "unable to count tracks" );
160 goto error;
161 }
162 else if( p_sys->i_titles <= 1 )
163 {
164 msg_Err( p_access, "no movie tracks found" );
165 goto error;
166 }
167
168 /* The first title isn't usable */
169 p_sys->i_titles--;
170
171 for( int i = 0; i < p_sys->i_titles; i++ )
172 {
173 msg_Dbg( p_access, "title[%d] start=%d", i, p_sys->p_sectors[1+i] );
174 msg_Dbg( p_access, "title[%d] end=%d", i, p_sys->p_sectors[i+2] );
175 }
176
177 /* Map entry points into chapters */
178 if( EntryPoints( p_access ) )
179 {
180 msg_Warn( p_access, "could not read entry points, will not use them" );
181 }
182
183 /* Starting title/chapter and sector */
184 if( i_title >= p_sys->i_titles )
185 i_title = 0;
186 if( (unsigned)i_chapter >= p_sys->titles[i_title].count )
187 i_chapter = 0;
188
189 p_sys->i_sector = p_sys->p_sectors[1+i_title];
190 if( i_chapter > 0 )
191 p_sys->i_sector += p_sys->titles[i_title].seekpoints[i_chapter]
192 / VCD_DATA_SIZE;
193
194 /* p_access */
195 p_access->pf_read = NULL;
196 p_access->pf_block = Block;
197 p_access->pf_control = Control;
198 p_access->pf_seek = Seek;
199
200 p_sys->i_current_title = i_title;
201 p_sys->i_current_seekpoint = i_chapter;
202 p_sys->offset = (uint64_t)(p_sys->i_sector - p_sys->p_sectors[1+i_title]) *
203 VCD_DATA_SIZE;
204
205 return VLC_SUCCESS;
206
207 error:
208 ioctl_Close( VLC_OBJECT(p_access), vcddev );
209 free( p_sys );
210 return VLC_EGENERIC;
211 }
212
213 /*****************************************************************************
214 * Close: closes vcd
215 *****************************************************************************/
Close(vlc_object_t * p_this)216 static void Close( vlc_object_t *p_this )
217 {
218 stream_t *p_access = (stream_t *)p_this;
219 access_sys_t *p_sys = p_access->p_sys;
220
221 for( size_t i = 0; i < ARRAY_SIZE(p_sys->titles); i++ )
222 free( p_sys->titles[i].seekpoints );
223
224 ioctl_Close( p_this, p_sys->vcddev );
225 free( p_sys );
226 }
227
228 /*****************************************************************************
229 * Control:
230 *****************************************************************************/
Control(stream_t * p_access,int i_query,va_list args)231 static int Control( stream_t *p_access, int i_query, va_list args )
232 {
233 access_sys_t *p_sys = p_access->p_sys;
234 input_title_t ***ppp_title;
235
236 switch( i_query )
237 {
238 /* */
239 case STREAM_CAN_SEEK:
240 case STREAM_CAN_FASTSEEK:
241 case STREAM_CAN_PAUSE:
242 case STREAM_CAN_CONTROL_PACE:
243 *va_arg( args, bool* ) = true;
244 break;
245
246 case STREAM_GET_SIZE:
247 {
248 int i = p_sys->i_current_title;
249
250 *va_arg( args, uint64_t * ) =
251 (p_sys->p_sectors[i + 2] - p_sys->p_sectors[i + 1])
252 * (uint64_t)VCD_DATA_SIZE;
253 break;
254 }
255
256 /* */
257 case STREAM_GET_PTS_DELAY:
258 *va_arg( args, int64_t * ) = INT64_C(1000)
259 * var_InheritInteger(p_access, "disc-caching");
260 break;
261
262 /* */
263 case STREAM_SET_PAUSE_STATE:
264 break;
265
266 case STREAM_GET_TITLE_INFO:
267 ppp_title = va_arg( args, input_title_t*** );
268 /* Duplicate title infos */
269 *ppp_title = vlc_alloc( p_sys->i_titles, sizeof(input_title_t *) );
270 if (!*ppp_title)
271 return VLC_ENOMEM;
272 *va_arg( args, int* ) = p_sys->i_titles;
273 for( int i = 0; i < p_sys->i_titles; i++ )
274 (*ppp_title)[i] = vlc_input_title_New();
275 break;
276
277 case STREAM_GET_TITLE:
278 *va_arg( args, unsigned * ) = p_sys->i_current_title;
279 break;
280
281 case STREAM_GET_SEEKPOINT:
282 *va_arg( args, unsigned * ) = p_sys->i_current_seekpoint;
283 break;
284
285 case STREAM_GET_CONTENT_TYPE:
286 *va_arg( args, char ** ) = strdup("video/MP2P");
287 break;
288
289 case STREAM_SET_TITLE:
290 {
291 int i = va_arg( args, int );
292 if( i != p_sys->i_current_title )
293 {
294 /* Update info */
295 p_sys->offset = 0;
296 p_sys->i_current_title = i;
297 p_sys->i_current_seekpoint = 0;
298
299 /* Next sector to read */
300 p_sys->i_sector = p_sys->p_sectors[1+i];
301 }
302 break;
303 }
304
305 case STREAM_SET_SEEKPOINT:
306 {
307 int i = va_arg( args, int );
308 unsigned i_title = p_sys->i_current_title;
309
310 if( p_sys->titles[i_title].count > 0 )
311 {
312 p_sys->i_current_seekpoint = i;
313
314 p_sys->i_sector = p_sys->p_sectors[1 + i_title] +
315 p_sys->titles[i_title].seekpoints[i] / VCD_DATA_SIZE;
316
317 p_sys->offset = (uint64_t)(p_sys->i_sector -
318 p_sys->p_sectors[1 + i_title]) * VCD_DATA_SIZE;
319 }
320 break;
321 }
322
323 default:
324 return VLC_EGENERIC;
325 }
326 return VLC_SUCCESS;
327 }
328
329 /*****************************************************************************
330 * Block:
331 *****************************************************************************/
Block(stream_t * p_access,bool * restrict eof)332 static block_t *Block( stream_t *p_access, bool *restrict eof )
333 {
334 access_sys_t *p_sys = p_access->p_sys;
335 int i_blocks = VCD_BLOCKS_ONCE;
336 block_t *p_block;
337
338 /* Check end of title */
339 while( p_sys->i_sector >= p_sys->p_sectors[p_sys->i_current_title + 2] )
340 {
341 if( p_sys->i_current_title + 2 >= p_sys->i_titles )
342 {
343 *eof = true;
344 return NULL;
345 }
346
347 p_sys->i_current_title++;
348 p_sys->i_current_seekpoint = 0;
349 p_sys->offset = 0;
350 }
351
352 /* Don't read after the end of a title */
353 if( p_sys->i_sector + i_blocks >=
354 p_sys->p_sectors[p_sys->i_current_title + 2] )
355 {
356 i_blocks = p_sys->p_sectors[p_sys->i_current_title + 2 ] - p_sys->i_sector;
357 }
358
359 /* Do the actual reading */
360 if( i_blocks < 0 || !( p_block = block_Alloc( i_blocks * VCD_DATA_SIZE ) ) )
361 {
362 msg_Err( p_access, "cannot get a new block of size: %i",
363 i_blocks * VCD_DATA_SIZE );
364 return NULL;
365 }
366
367 if( ioctl_ReadSectors( VLC_OBJECT(p_access), p_sys->vcddev,
368 p_sys->i_sector, p_block->p_buffer, i_blocks, VCD_TYPE ) < 0 )
369 {
370 msg_Err( p_access, "cannot read sector %i", p_sys->i_sector );
371 block_Release( p_block );
372
373 /* Try to skip one sector (in case of bad sectors) */
374 p_sys->offset += VCD_DATA_SIZE;
375 p_sys->i_sector++;
376 return NULL;
377 }
378
379 /* Update seekpoints */
380 for( int i_read = 0; i_read < i_blocks; i_read++ )
381 {
382 int i_title = p_sys->i_current_title;
383
384 if( p_sys->titles[i_title].count > 0 &&
385 p_sys->i_current_seekpoint + 1 < p_sys->titles[i_title].count &&
386 (p_sys->offset + i_read * VCD_DATA_SIZE) >=
387 p_sys->titles[i_title].seekpoints[p_sys->i_current_seekpoint + 1] )
388 {
389 msg_Dbg( p_access, "seekpoint change" );
390 p_sys->i_current_seekpoint++;
391 }
392 }
393
394 /* Update a few values */
395 p_sys->offset += p_block->i_buffer;
396 p_sys->i_sector += i_blocks;
397
398 return p_block;
399 }
400
401 /*****************************************************************************
402 * Seek:
403 *****************************************************************************/
Seek(stream_t * p_access,uint64_t i_pos)404 static int Seek( stream_t *p_access, uint64_t i_pos )
405 {
406 access_sys_t *p_sys = p_access->p_sys;
407 int i_title = p_sys->i_current_title;
408 unsigned i_seekpoint;
409
410 /* Next sector to read */
411 p_sys->offset = i_pos;
412 p_sys->i_sector = i_pos / VCD_DATA_SIZE + p_sys->p_sectors[i_title + 1];
413
414 /* Update current seekpoint */
415 for( i_seekpoint = 0; i_seekpoint < p_sys->titles[i_title].count; i_seekpoint++ )
416 {
417 if( i_seekpoint + 1 >= p_sys->titles[i_title].count ) break;
418 if( 0 < p_sys->titles[i_title].seekpoints[i_seekpoint + 1] &&
419 i_pos < p_sys->titles[i_title].seekpoints[i_seekpoint + 1] ) break;
420 }
421
422 if( i_seekpoint != p_sys->i_current_seekpoint )
423 {
424 msg_Dbg( p_access, "seekpoint change" );
425 p_sys->i_current_seekpoint = i_seekpoint;
426 }
427
428 return VLC_SUCCESS;
429 }
430
431 /*****************************************************************************
432 * EntryPoints: Reads the information about the entry points on the disc.
433 *****************************************************************************/
EntryPoints(stream_t * p_access)434 static int EntryPoints( stream_t *p_access )
435 {
436 access_sys_t *p_sys = p_access->p_sys;
437 uint8_t sector[VCD_DATA_SIZE];
438
439 entries_sect_t entries;
440 int i_nb;
441
442 /* Read the entry point sector */
443 if( ioctl_ReadSectors( VLC_OBJECT(p_access), p_sys->vcddev,
444 VCD_ENTRIES_SECTOR, sector, 1, VCD_TYPE ) < 0 )
445 {
446 msg_Err( p_access, "could not read entry points sector" );
447 return VLC_EGENERIC;
448 }
449 memcpy( &entries, sector, CD_SECTOR_SIZE );
450
451 i_nb = GetWBE( &entries.i_entries_nb );
452 if( i_nb > 500 )
453 {
454 msg_Err( p_access, "invalid entry points number" );
455 return VLC_EGENERIC;
456 }
457
458 if( strncmp( entries.psz_id, "ENTRYVCD", sizeof( entries.psz_id ) ) &&
459 strncmp( entries.psz_id, "ENTRYSVD", sizeof( entries.psz_id ) ) )
460 {
461 msg_Err( p_access, "unrecognized entry points format" );
462 return VLC_EGENERIC;
463 }
464
465 for( int i = 0; i < i_nb; i++ )
466 {
467 const int i_title = BCD_TO_BIN(entries.entry[i].i_track) - 2;
468 const int i_sector =
469 (MSF_TO_LBA2( BCD_TO_BIN( entries.entry[i].msf.minute ),
470 BCD_TO_BIN( entries.entry[i].msf.second ),
471 BCD_TO_BIN( entries.entry[i].msf.frame ) ));
472 if( i_title < 0 ) continue; /* Should not occur */
473 if( i_title >= p_sys->i_titles ) continue;
474
475 msg_Dbg( p_access, "Entry[%d] title=%d sector=%d",
476 i, i_title, i_sector );
477
478 p_sys->titles[i_title].seekpoints = xrealloc(
479 p_sys->titles[i_title].seekpoints,
480 sizeof( uint64_t ) * (p_sys->titles[i_title].count + 1) );
481 p_sys->titles[i_title].seekpoints[p_sys->titles[i_title].count++] =
482 (i_sector - p_sys->p_sectors[i_title+1]) * VCD_DATA_SIZE;
483 }
484
485 return VLC_SUCCESS;
486 }
487
488