vcd.c 15.8 KB
Newer Older
1
2
3
/*****************************************************************************
 * vcd.c : VCD input module for vlc
 *****************************************************************************
4
 * Copyright (C) 2000-2004 the VideoLAN team
5
 * $Id$
6
7
8
9
10
11
12
 *
 * Author: Johan Bilien <jobi@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14
15
16
17
18
19
20
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
27
28
29
30
31
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>

#include <vlc/vlc.h>
#include <vlc/input.h>

32
33
34
35
36
37
38
#include "cdrom.h"

/*****************************************************************************
 * Module descriptior
 *****************************************************************************/
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
39

40
41
42
43
44
#define CACHING_TEXT N_("Caching value in ms")
#define CACHING_LONGTEXT N_( \
    "Allows you to modify the default caching value for cdda streams. This " \
    "value should be set in milliseconds units." )

45
vlc_module_begin();
46
    set_shortname( _("VCD"));
47
    set_description( _("VCD input") );
48
    set_capability( "access2", 60 );
49
    set_callbacks( Open, Close );
zorglub's avatar
zorglub committed
50
51
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_ACCESS );
52

53
    add_usage_hint( N_("[vcd:][device][@[title][,[chapter]]]") );
54
55
56
    add_integer( "vcd-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT,
                 CACHING_LONGTEXT, VLC_TRUE );
    add_shortcut( "vcd" );
57
58
    add_shortcut( "svcd" );
vlc_module_end();
59

60
61
62
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
63
64
65
66
67

/* how many blocks VCDRead will read in each loop */
#define VCD_BLOCKS_ONCE 20
#define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * VCD_DATA_SIZE)

68
struct access_sys_t
gbazin's avatar
   
gbazin committed
69
70
{
    vcddev_t    *vcddev;                            /* vcd device descriptor */
71
72
73
74
75
76

    /* Title infos */
    int           i_titles;
    input_title_t *title[99];            /* No more that 99 track in a vcd ? */


gbazin's avatar
   
gbazin committed
77
    int         i_sector;                                  /* Current Sector */
78
    int         *p_sectors;                                 /* Track sectors */
79
};
gbazin's avatar
   
gbazin committed
80

81
82
83
84
static block_t *Block( access_t * );
static int      Seek( access_t *, int64_t );
static int      Control( access_t *, int, va_list );
static int      EntryPoints( access_t * );
85
86
87
88

/*****************************************************************************
 * VCDOpen: open vcd
 *****************************************************************************/
89
static int Open( vlc_object_t *p_this )
90
{
91
92
93
    access_t     *p_access = (access_t *)p_this;
    access_sys_t *p_sys;
    char *psz_dup = strdup( p_access->psz_path );
94
    char *psz;
95
96
97
98
    int i_title = 0;
    int i_chapter = 0;
    int i;
    vcddev_t *vcddev;
99

100
101
    /* Command line: vcd://[dev_path][@title[,chapter]] */
    if( ( psz = strchr( psz_dup, '@' ) ) )
102
    {
103
        *psz++ = '\0';
104

105
106
        i_title = strtol( psz, &psz, 0 );
        if( *psz )
107
            i_chapter = strtol( psz+1, &psz, 0 );
108
109
    }

110
    if( *psz_dup == '\0' )
111
    {
112
113
114
        free( psz_dup );

        /* Only when selected */
115
116
        if( strcmp( p_access->psz_access, "vcd" ) &&
            strcmp( p_access->psz_access, "svcd" ) )
117
118
            return VLC_EGENERIC;

119
        psz_dup = var_CreateGetString( p_access, "vcd" );
120
        if( *psz_dup == '\0' )
121
        {
122
123
            free( psz_dup );
            return VLC_EGENERIC;
124
125
126
        }
    }

127
128
129
130
131
#ifdef WIN32
    if( psz_dup[0] && psz_dup[1] == ':' &&
        psz_dup[2] == '\\' && psz_dup[3] == '\0' ) psz_dup[2] = '\0';
#endif

gbazin's avatar
   
gbazin committed
132
    /* Open VCD */
133
    if( !(vcddev = ioctl_Open( p_this, psz_dup )) )
gbazin's avatar
   
gbazin committed
134
    {
135
136
        free( psz_dup );
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
137
    }
138
    free( psz_dup );
139

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    /* Set up p_access */
    p_access->pf_read = NULL;
    p_access->pf_block = Block;
    p_access->pf_control = Control;
    p_access->pf_seek = Seek;
    p_access->info.i_update = 0;
    p_access->info.i_size = 0;
    p_access->info.i_pos = 0;
    p_access->info.b_eof = VLC_FALSE;
    p_access->info.i_title = 0;
    p_access->info.i_seekpoint = 0;
    p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
    memset( p_sys, 0, sizeof( access_sys_t ) );
    p_sys->vcddev = vcddev;

    /* We read the Table Of Content information */
    p_sys->i_titles = ioctl_GetTracksMap( VLC_OBJECT(p_access),
                                          p_sys->vcddev, &p_sys->p_sectors );
    if( p_sys->i_titles < 0 )
159
    {
160
161
        msg_Err( p_access, "unable to count tracks" );
        goto error;
162
    }
163
164
165
166
167
168
169
    else if( p_sys->i_titles <= 1 )
    {
        msg_Err( p_access, "no movie tracks found" );
        goto error;
    }
    /* The first title isn't usable */
    p_sys->i_titles--;
170

171
172
173
174
    /* Build title table */
    for( i = 0; i < p_sys->i_titles; i++ )
    {
        input_title_t *t = p_sys->title[i] = vlc_input_title_New();
175

hartman's avatar
hartman committed
176
177
        msg_Dbg( p_access, "title[%d] start=%d\n", i, p_sys->p_sectors[1+i] );
        msg_Dbg( p_access, "title[%d] end=%d\n", i, p_sys->p_sectors[i+2] );
178

179
180
181
        t->i_size = ( p_sys->p_sectors[i+2] - p_sys->p_sectors[i+1] ) *
                    (int64_t)VCD_DATA_SIZE;
    }
182

183
184
185
186
    /* Map entry points into chapters */
    if( EntryPoints( p_access ) )
    {
        msg_Warn( p_access, "could not read entry points, will not use them" );
187
188
    }

189
190
191
192
193
    /* Starting title/chapter and sector */
    if( i_title >= p_sys->i_titles )
        i_title = 0;
    if( i_chapter >= p_sys->title[i_title]->i_seekpoint )
        i_chapter = 0;
194

195
196
    p_sys->i_sector = p_sys->p_sectors[1+i_title];
    if( i_chapter > 0 )
197
    {
198
199
200
        p_sys->i_sector +=
            ( p_sys->title[i_title]->seekpoint[i_chapter]->i_byte_offset /
              VCD_DATA_SIZE );
201
    }
202
203
204
    p_access->info.i_title = i_title;
    p_access->info.i_seekpoint = i_chapter;
    p_access->info.i_size = p_sys->title[i_title]->i_size;
205
206
    p_access->info.i_pos = ( p_sys->i_sector - p_sys->p_sectors[1+i_title] ) *
        VCD_DATA_SIZE;
207

208
    p_access->psz_demux = strdup( "ps" );
209

210
    return VLC_SUCCESS;
211

212
213
214
215
216
error:
    ioctl_Close( VLC_OBJECT(p_access), p_sys->vcddev );
    free( p_sys );
    return VLC_EGENERIC;
}
217

218
219
220
221
222
223
224
225
226
227
228
/*****************************************************************************
 * Close: closes vcd
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
{
    access_t     *p_access = (access_t *)p_this;
    access_sys_t *p_sys = p_access->p_sys;

    ioctl_Close( p_this, p_sys->vcddev );
    free( p_sys );
}
229

230
231
232
233
234
235
236
237
238
239
240
241
242
/*****************************************************************************
 * Control:
 *****************************************************************************/
static int Control( access_t *p_access, int i_query, va_list args )
{
    access_sys_t *p_sys = p_access->p_sys;
    vlc_bool_t   *pb_bool;
    int          *pi_int;
    int64_t      *pi_64;
    input_title_t ***ppp_title;
    int i;

    switch( i_query )
243
    {
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
        /* */
        case ACCESS_CAN_SEEK:
        case ACCESS_CAN_FASTSEEK:
        case ACCESS_CAN_PAUSE:
        case ACCESS_CAN_CONTROL_PACE:
            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
            *pb_bool = VLC_TRUE;
            break;

        /* */
        case ACCESS_GET_MTU:
            pi_int = (int*)va_arg( args, int * );
            *pi_int = VCD_DATA_ONCE;
            break;

        case ACCESS_GET_PTS_DELAY:
            pi_64 = (int64_t*)va_arg( args, int64_t * );
261
            *pi_64 = var_GetInteger( p_access, "vcd-caching" ) * 1000;
262
263
264
265
266
267
268
269
270
271
272
273
            break;

        /* */
        case ACCESS_SET_PAUSE_STATE:
            break;

        case ACCESS_GET_TITLE_INFO:
            ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
            pi_int    = (int*)va_arg( args, int* );

            /* Duplicate title infos */
            *pi_int = p_sys->i_titles;
274
            *ppp_title = malloc( sizeof(input_title_t **) * p_sys->i_titles );
275
276
277
278
279
            for( i = 0; i < p_sys->i_titles; i++ )
            {
                (*ppp_title)[i] = vlc_input_title_Duplicate( p_sys->title[i] );
            }
            break;
280

281
282
283
284
285
        case ACCESS_SET_TITLE:
            i = (int)va_arg( args, int );
            if( i != p_access->info.i_title )
            {
                /* Update info */
286
287
                p_access->info.i_update |=
                  INPUT_UPDATE_TITLE|INPUT_UPDATE_SEEKPOINT|INPUT_UPDATE_SIZE;
288
289
290
291
292
293
294
295
296
                p_access->info.i_title = i;
                p_access->info.i_seekpoint = 0;
                p_access->info.i_size = p_sys->title[i]->i_size;
                p_access->info.i_pos  = 0;

                /* Next sector to read */
                p_sys->i_sector = p_sys->p_sectors[1+i];
            }
            break;
297

298
299
300
301
302
303
304
305
306
307
        case ACCESS_SET_SEEKPOINT:
        {
            input_title_t *t = p_sys->title[p_access->info.i_title];
            i = (int)va_arg( args, int );
            if( t->i_seekpoint > 0 )
            {
                p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT;
                p_access->info.i_seekpoint = i;

                p_sys->i_sector = p_sys->p_sectors[1+p_access->info.i_title] +
308
                    t->seekpoint[i]->i_byte_offset / VCD_DATA_SIZE;
309

310
311
                p_access->info.i_pos = (int64_t)(p_sys->i_sector -
                    p_sys->p_sectors[1+p_access->info.i_title]) *VCD_DATA_SIZE;
312
313
314
315
            }
            return VLC_SUCCESS;
        }

316
317
318
        case ACCESS_SET_PRIVATE_ID_STATE:
            return VLC_EGENERIC;

319
        default:
320
            msg_Warn( p_access, "unimplemented query in control" );
321
            return VLC_EGENERIC;
322
323

    }
324
325
326
327
328
329
330
331
332
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Block:
 *****************************************************************************/
static block_t *Block( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;
333
334
335
    int i_blocks = VCD_BLOCKS_ONCE;
    block_t *p_block;
    int i_read;
336

337
338
    /* Check end of file */
    if( p_access->info.b_eof ) return NULL;
339

340
341
342
343
344
345
346
347
    /* Check end of title */
    while( p_sys->i_sector >= p_sys->p_sectors[p_access->info.i_title + 2] )
    {
        if( p_access->info.i_title + 2 >= p_sys->i_titles )
        {
            p_access->info.b_eof = VLC_TRUE;
            return NULL;
        }
348

349
350
351
352
353
354
355
356
357
358
359
360
        p_access->info.i_update |=
            INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT | INPUT_UPDATE_SIZE;
        p_access->info.i_title++;
        p_access->info.i_seekpoint = 0;
        p_access->info.i_size =
            p_sys->title[p_access->info.i_title]->i_size;
        p_access->info.i_pos = 0;
    }

    /* Don't read after the end of a title */
    if( p_sys->i_sector + i_blocks >=
        p_sys->p_sectors[p_access->info.i_title + 2] )
361
    {
362
363
        i_blocks = p_sys->p_sectors[p_access->info.i_title + 2 ] -
                   p_sys->i_sector;
364
365
    }

366
367
368
369
    /* Do the actual reading */
    if( !( p_block = block_New( p_access, i_blocks * VCD_DATA_SIZE ) ) )
    {
        msg_Err( p_access, "cannot get a new block of size: %i",
370
                 i_blocks * VCD_DATA_SIZE );
371
372
        return NULL;
    }
373

374
375
    if( ioctl_ReadSectors( VLC_OBJECT(p_access), p_sys->vcddev,
            p_sys->i_sector, p_block->p_buffer, i_blocks, VCD_TYPE ) < 0 )
376
    {
377
        msg_Err( p_access, "cannot read sector %i", p_sys->i_sector );
378
        block_Release( p_block );
379
380
381
382

        /* Try to skip one sector (in case of bad sectors) */
        p_sys->i_sector++;
        p_access->info.i_pos += VCD_DATA_SIZE;
383
        return NULL;
384
385
    }

386
    /* Update seekpoints */
387
    for( i_read = 0; i_read < i_blocks; i_read++ )
388
    {
389
390
        input_title_t *t = p_sys->title[p_access->info.i_title];

391
392
        if( t->i_seekpoint > 0 &&
            p_access->info.i_seekpoint + 1 < t->i_seekpoint &&
393
            p_access->info.i_pos + i_read * VCD_DATA_SIZE >=
394
            t->seekpoint[p_access->info.i_seekpoint+1]->i_byte_offset )
395
        {
396
            msg_Dbg( p_access, "seekpoint change" );
397
398
399
400
            p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT;
            p_access->info.i_seekpoint++;
        }
    }
gbazin's avatar
   
gbazin committed
401

402
    /* Update a few values */
403
    p_sys->i_sector += i_blocks;
404
    p_access->info.i_pos += p_block->i_buffer;
405
406

    return p_block;
407
408
409
}

/*****************************************************************************
410
 * Seek:
411
 *****************************************************************************/
412
static int Seek( access_t *p_access, int64_t i_pos )
413
{
414
415
416
417
418
    access_sys_t *p_sys = p_access->p_sys;
    input_title_t *t = p_sys->title[p_access->info.i_title];
    int i_seekpoint;

    /* Next sector to read */
419
    p_access->info.i_pos = i_pos;
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
    p_sys->i_sector = i_pos / VCD_DATA_SIZE +
        p_sys->p_sectors[p_access->info.i_title + 1];

    /* Update current seekpoint */
    for( i_seekpoint = 0; i_seekpoint < t->i_seekpoint; i_seekpoint++ )
    {
        if( i_seekpoint + 1 >= t->i_seekpoint ) break;
        if( i_pos < t->seekpoint[i_seekpoint + 1]->i_byte_offset ) break;
    }

    if( i_seekpoint != p_access->info.i_seekpoint )
    {
        msg_Dbg( p_access, "seekpoint change" );
        p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT;
        p_access->info.i_seekpoint = i_seekpoint;
    }

    return VLC_SUCCESS;
438
}
439

440
/*****************************************************************************
441
 * EntryPoints: Reads the information about the entry points on the disc.
442
443
444
445
446
447
448
 *****************************************************************************/
static int EntryPoints( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;
    uint8_t      sector[VCD_DATA_SIZE];

    entries_sect_t entries;
449
    int i_nb, i;
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482

    /* Read the entry point sector */
    if( ioctl_ReadSectors( VLC_OBJECT(p_access), p_sys->vcddev,
        VCD_ENTRIES_SECTOR, sector, 1, VCD_TYPE ) < 0 )
    {
        msg_Err( p_access, "could not read entry points sector" );
        return VLC_EGENERIC;
    }
    memcpy( &entries, sector, CD_SECTOR_SIZE );

    i_nb = GetWBE( &entries.i_entries_nb );
    if( i_nb > 500 )
    {
        msg_Err( p_access, "invalid entry points number" );
        return VLC_EGENERIC;
    }

    if( strncmp( entries.psz_id, "ENTRYVCD", sizeof( entries.psz_id ) ) &&
        strncmp( entries.psz_id, "ENTRYSVD", sizeof( entries.psz_id ) ) )
    {
        msg_Err( p_access, "unrecognized entry points format" );
        return VLC_EGENERIC;
    }

    for( i = 0; i < i_nb; i++ )
    {
        const int i_title = BCD_TO_BIN(entries.entry[i].i_track) - 2;
        const int i_sector =
            (MSF_TO_LBA2( BCD_TO_BIN( entries.entry[i].msf.minute ),
                          BCD_TO_BIN( entries.entry[i].msf.second ),
                          BCD_TO_BIN( entries.entry[i].msf.frame  ) ));
        seekpoint_t *s;

483
484
        if( i_title < 0 ) continue;   /* Should not occur */
        if( i_title >= p_sys->i_titles ) continue;
485

486
        msg_Dbg( p_access, "Entry[%d] title=%d sector=%d\n",
487
488
489
                 i, i_title, i_sector );

        s = vlc_seekpoint_New();
490
491
        s->i_byte_offset = (i_sector - p_sys->p_sectors[i_title+1]) *
            VCD_DATA_SIZE;
492

493
494
        TAB_APPEND( p_sys->title[i_title]->i_seekpoint,
                    p_sys->title[i_title]->seekpoint, s );
495
496
    }

497
    return VLC_SUCCESS;
498
}