cdda.c 20.9 KB
Newer Older
Gildas Bazin's avatar
 
Gildas Bazin committed
1 2 3
/*****************************************************************************
 * cdda.c : CD digital audio input module for vlc
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2000, 2003-2006, 2008-2009 VLC authors and VideoLAN
5
 * $Id$
Gildas Bazin's avatar
 
Gildas Bazin committed
6 7 8 9
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *          Gildas Bazin <gbazin@netcourrier.com>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
10 11 12
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
Gildas Bazin's avatar
 
Gildas Bazin committed
13 14 15 16
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
Gildas Bazin's avatar
 
Gildas Bazin committed
19
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
20 21 22
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Gildas Bazin's avatar
 
Gildas Bazin committed
23 24
 *****************************************************************************/

25 26
/**
 * Todo:
27
 *   - Improve CDDB support (non-blocking, ...)
28 29 30
 *   - Fix tracknumber in MRL
 */

Gildas Bazin's avatar
 
Gildas Bazin committed
31 32 33 34
/*****************************************************************************
 * Preamble
 *****************************************************************************/

35 36 37
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
38

39
#include <assert.h>
40 41 42
#include <math.h>
#include <stdlib.h>
#include <string.h>
43

44
#include <vlc_common.h>
45
#include <vlc_demux.h>
46
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
47 48
#include <vlc_input.h>
#include <vlc_access.h>
49
#include <vlc_meta.h>
50
#include <vlc_charset.h> /* ToLocaleDup */
51
#include <vlc_url.h>
Gildas Bazin's avatar
 
Gildas Bazin committed
52

53
#include "vcd/cdrom.h"  /* For CDDA_DATA_SIZE */
Gildas Bazin's avatar
 
Gildas Bazin committed
54

55
#ifdef HAVE_LIBCDDB
56 57
 #include <cddb/cddb.h>
 #include <errno.h>
58 59
#endif

60 61
static vcddev_t *DiscOpen(vlc_object_t *obj, const char *location,
                         const char *path, unsigned *restrict trackp)
62
{
63
    char *devpath;
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
    *trackp = var_InheritInteger(obj, "cdda-track");

    if (path != NULL)
        devpath = ToLocaleDup(path);
    else if (location[0] != '\0')
    {
#if (DIR_SEP_CHAR == '/')
        char *dec = vlc_uri_decode_duplicate(location);
        if (dec == NULL)
            return NULL;

        /* GNOME CDDA syntax */
        const char *sl = strrchr(dec, '/');
        if (sl != NULL)
        {
            if (sscanf(sl, "/Track %2u", trackp) == 1)
                dec[sl - dec] = '\0';
            else
                *trackp = 0;
        }

        if (unlikely(asprintf(&devpath, "/dev/%s", dec) == -1))
            devpath = NULL;
        free(dec);
#else
        (void) location;
        return NULL;
#endif
    }
94
    else
95 96 97
        devpath = var_InheritString(obj, "cd-audio");

    if (devpath == NULL)
98 99 100 101
        return NULL;

#if defined (_WIN32) || defined (__OS2__)
    /* Trim backslash after drive letter */
102 103
    if (devpath[0] != '\0' && !strcmp(&devpath[1], ":" DIR_SEP))
        devpath[2] = '\0';
104 105 106
#endif

    /* Open CDDA */
107
    vcddev_t *dev = ioctl_Open(obj, devpath);
108
    if (dev == NULL)
109 110
        msg_Warn(obj, "cannot open disc %s", devpath);
    free(devpath);
111 112 113 114

    return dev;
}

115
/* how many blocks Demux() will read in each iteration */
Laurent Aimar's avatar
Laurent Aimar committed
116 117
#define CDDA_BLOCKS_ONCE 20

118
typedef struct
Laurent Aimar's avatar
Laurent Aimar committed
119 120
{
    vcddev_t    *vcddev;                            /* vcd device descriptor */
121 122
    es_out_id_t *es;
    date_t       pts;
Laurent Aimar's avatar
Laurent Aimar committed
123

124 125 126
    unsigned start; /**< Track first sector */
    unsigned length; /**< Track total sectors */
    unsigned position; /**< Current offset within track sectors */
127
} demux_sys_t;
Laurent Aimar's avatar
Laurent Aimar committed
128

129
static int Demux(demux_t *demux)
Gildas Bazin's avatar
 
Gildas Bazin committed
130
{
131 132
    demux_sys_t *sys = demux->p_sys;
    unsigned count = CDDA_BLOCKS_ONCE;
Gildas Bazin's avatar
 
Gildas Bazin committed
133

134 135
    if (sys->position >= sys->length)
        return VLC_DEMUXER_EOF;
136

137 138
    if (sys->position + count >= sys->length)
        count = sys->length - sys->position;
Gildas Bazin's avatar
 
Gildas Bazin committed
139

140 141 142
    block_t *block = block_Alloc(count * CDDA_DATA_SIZE);
    if (unlikely(block == NULL))
        return VLC_DEMUXER_EOF;
Gildas Bazin's avatar
 
Gildas Bazin committed
143

144 145 146
    if (ioctl_ReadSectors(VLC_OBJECT(demux), sys->vcddev,
                          sys->start + sys->position,
                          block->p_buffer, count, CDDA_TYPE) < 0)
Gildas Bazin's avatar
 
Gildas Bazin committed
147
    {
148 149
        msg_Err(demux, "cannot read sector %u", sys->position);
        block_Release(block);
150

151 152 153
        /* Skip potentially bad sector */
        sys->position++;
        return VLC_DEMUXER_SUCCESS;
Gildas Bazin's avatar
 
Gildas Bazin committed
154 155
    }

156
    sys->position += count;
157

158 159 160
    block->i_nb_samples = block->i_buffer / 4;
    block->i_dts = block->i_pts = VLC_TS_0 + date_Get(&sys->pts);
    date_Increment(&sys->pts, block->i_nb_samples);
Gildas Bazin's avatar
 
Gildas Bazin committed
161

162
    es_out_Send(demux->out, sys->es, block);
163
    es_out_SetPCR(demux->out, VLC_TS_0 + date_Get(&sys->pts));
164
    return VLC_DEMUXER_SUCCESS;
165
}
Gildas Bazin's avatar
 
Gildas Bazin committed
166

167
static int DemuxControl(demux_t *demux, int query, va_list args)
168
{
169 170 171 172 173
    demux_sys_t *sys = demux->p_sys;

    /* One sector is 40000/3 µs */
    static_assert (CDDA_DATA_SIZE * CLOCK_FREQ * 3 ==
                   4 * 44100 * INT64_C(40000), "Wrong time/sector ratio");
174

175
    switch (query)
176
    {
177 178 179 180
        case DEMUX_CAN_SEEK:
        case DEMUX_CAN_PAUSE:
        case DEMUX_CAN_CONTROL_PACE:
            *va_arg(args, bool*) = true;
181
            break;
182 183 184
        case DEMUX_GET_PTS_DELAY:
            *va_arg(args, int64_t *) =
                INT64_C(1000) * var_InheritInteger(demux, "disc-caching");
185
            break;
186 187

        case DEMUX_SET_PAUSE_STATE:
188 189
            break;

190 191 192 193
        case DEMUX_GET_POSITION:
            *va_arg(args, double *) = (double)(sys->position)
                                      / (double)(sys->length);
            break;
194

195 196 197 198 199 200 201 202 203 204 205 206
        case DEMUX_SET_POSITION:
            sys->position = lround(va_arg(args, double) * sys->length);
            break;

        case DEMUX_GET_LENGTH:
            *va_arg(args, mtime_t *) = (INT64_C(40000) * sys->length) / 3;
            break;
        case DEMUX_GET_TIME:
            *va_arg(args, mtime_t *) = (INT64_C(40000) * sys->position) / 3;
            break;
        case DEMUX_SET_TIME:
            sys->position = (va_arg(args, mtime_t) * 3) / INT64_C(40000);
207 208
            break;

209 210 211 212 213 214
        default:
            return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

215
static int DemuxOpen(vlc_object_t *obj, vcddev_t *dev, unsigned track)
216 217 218
{
    demux_t *demux = (demux_t *)obj;

219
    if (demux->out == NULL)
220
        goto error;
221

222
    demux_sys_t *sys = vlc_obj_malloc(obj, sizeof (*sys));
223
    if (unlikely(sys == NULL))
224
        goto error;
225

226 227
    demux->p_sys = sys;
    sys->vcddev = dev;
228 229 230 231 232 233
    sys->start = var_InheritInteger(obj, "cdda-first-sector");
    sys->length = var_InheritInteger(obj, "cdda-last-sector") - sys->start;

    /* Track number in input item */
    if (sys->start == (unsigned)-1 || sys->length == (unsigned)-1)
    {
234
        int *sectors = NULL; /* Track sectors */
235
        unsigned titles = ioctl_GetTracksMap(obj, dev, &sectors);
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264

        if (track > titles)
        {
            msg_Err(obj, "invalid track number: %u/%u", track, titles);
            free(sectors);
            goto error;
        }

        sys->start = sectors[track - 1];
        sys->length = sectors[track] - sys->start;
        free(sectors);
    }

    es_format_t fmt;

    es_format_Init(&fmt, AUDIO_ES, VLC_CODEC_S16L);
    fmt.audio.i_rate = 44100;
    fmt.audio.i_channels = 2;
    sys->es = es_out_Add(demux->out, &fmt);

    date_Init(&sys->pts, 44100, 1);
    date_Set(&sys->pts, 0);

    sys->position = 0;
    demux->pf_demux = Demux;
    demux->pf_control = DemuxControl;
    return VLC_SUCCESS;

error:
265
    ioctl_Close(obj, dev);
266 267 268 269 270 271
    return VLC_EGENERIC;
}

/*****************************************************************************
 * Access: local prototypes
 *****************************************************************************/
272
typedef struct
273 274
{
    vcddev_t    *vcddev;                            /* vcd device descriptor */
275 276 277 278 279 280 281
    int         *p_sectors;                                 /* Track sectors */
    int          titles;
    int          cdtextc;
    vlc_meta_t **cdtextv;
#ifdef HAVE_LIBCDDB
    cddb_disc_t *cddb;
#endif
282
} access_sys_t;
283

284
#ifdef HAVE_LIBCDDB
285
static cddb_disc_t *GetCDDBInfo( vlc_object_t *obj, int i_titles, int *p_sectors )
286
{
287
    if( !var_InheritBool( obj, "metadata-network-access" ) )
288
    {
289
        msg_Dbg( obj, "album art policy set to manual: not fetching" );
290 291 292 293 294 295 296
        return NULL;
    }

    /* */
    cddb_conn_t *p_cddb = cddb_new();
    if( !p_cddb )
    {
297
        msg_Warn( obj, "unable to use CDDB" );
298 299 300 301 302 303 304
        return NULL;
    }

    /* */

    cddb_http_enable( p_cddb );

305
    char *psz_tmp = var_InheritString( obj, "cddb-server" );
306 307 308 309 310 311
    if( psz_tmp )
    {
        cddb_set_server_name( p_cddb, psz_tmp );
        free( psz_tmp );
    }

312
    cddb_set_server_port( p_cddb, var_InheritInteger( obj, "cddb-port" ) );
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335

    cddb_set_email_address( p_cddb, "vlc@videolan.org" );

    cddb_set_http_path_query( p_cddb, "/~cddb/cddb.cgi" );
    cddb_set_http_path_submit( p_cddb, "/~cddb/submit.cgi" );


    char *psz_cachedir;
    char *psz_temp = config_GetUserDir( VLC_CACHE_DIR );

    if( asprintf( &psz_cachedir, "%s" DIR_SEP "cddb", psz_temp ) > 0 ) {
        cddb_cache_enable( p_cddb );
        cddb_cache_set_dir( p_cddb, psz_cachedir );
        free( psz_cachedir );
    }
    free( psz_temp );

    cddb_set_timeout( p_cddb, 10 );

    /* */
    cddb_disc_t *p_disc = cddb_disc_new();
    if( !p_disc )
    {
336
        msg_Err( obj, "unable to create CDDB disc structure." );
337 338 339 340 341 342 343 344 345 346 347 348 349 350
        goto error;
    }

    int64_t i_length = 2000000; /* PreGap */
    for( int i = 0; i < i_titles; i++ )
    {
        cddb_track_t *t = cddb_track_new();
        cddb_track_set_frame_offset( t, p_sectors[i] + 150 );  /* Pregap offset */

        cddb_disc_add_track( p_disc, t );
        const int64_t i_size = ( p_sectors[i+1] - p_sectors[i] ) *
                               (int64_t)CDDA_DATA_SIZE;
        i_length += INT64_C(1000000) * i_size / 44100 / 4  ;

351
        msg_Dbg( obj, "Track %i offset: %i", i, p_sectors[i] + 150 );
352 353
    }

354
    msg_Dbg( obj, "Total length: %i", (int)(i_length/1000000) );
355 356 357 358
    cddb_disc_set_length( p_disc, (int)(i_length/1000000) );

    if( !cddb_disc_calc_discid( p_disc ) )
    {
359
        msg_Err( obj, "CDDB disc ID calculation failed" );
360 361 362 363 364 365
        goto error;
    }

    const int i_matches = cddb_query( p_cddb, p_disc );
    if( i_matches < 0 )
    {
366
        msg_Warn( obj, "CDDB error: %s", cddb_error_str(errno) );
367
        goto error;
368
    }
369 370
    else if( i_matches == 0 )
    {
371
        msg_Dbg( obj, "Couldn't find any matches in CDDB." );
372 373 374
        goto error;
    }
    else if( i_matches > 1 )
375
        msg_Warn( obj, "found %d matches in CDDB. Using first one.", i_matches );
376 377 378 379 380 381 382 383 384 385 386

    cddb_read( p_cddb, p_disc );

    cddb_destroy( p_cddb);
    return p_disc;

error:
    if( p_disc )
        cddb_disc_destroy( p_disc );
    cddb_destroy( p_cddb );
    return NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
387
}
388
#endif /* HAVE_LIBCDDB */
389

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
390
static void AccessGetMeta(stream_t *access, vlc_meta_t *meta)
391
{
392
    access_sys_t *sys = access->p_sys;
393

394
    vlc_meta_SetTitle(meta, "Audio CD");
395

396 397 398
    /* Retrieve CD-TEXT information */
    if (sys->cdtextc > 0 && sys->cdtextv[0] != NULL)
        vlc_meta_Merge(meta, sys->cdtextv[0]);
399

400 401 402 403 404
/* Return true if the given string is not NULL and not empty */
#define NONEMPTY( psz ) ( (psz) && *(psz) )
/* If the given string is NULL or empty, fill it by the return value of 'code' */
#define ON_EMPTY( psz, code ) do { if( !NONEMPTY( psz) ) { (psz) = code; } } while(0)

405
    /* Retrieve CDDB information (preferred over CD-TEXT) */
406
#ifdef HAVE_LIBCDDB
407
    if (sys->cddb != NULL)
408
    {
409
        const char *str = cddb_disc_get_title(sys->cddb);
410 411
        if (NONEMPTY(str))
            vlc_meta_SetTitle(meta, str);
412

413 414 415 416 417 418
        str = cddb_disc_get_genre(sys->cddb);
        if (NONEMPTY(str))
            vlc_meta_SetGenre(meta, str);

        const unsigned year = cddb_disc_get_year(sys->cddb);
        if (year != 0)
419
        {
420 421 422 423
            char yearbuf[5];

            snprintf(yearbuf, sizeof (yearbuf), "%u", year);
            vlc_meta_SetDate(meta, yearbuf);
424 425
        }

426 427 428
        /* Set artist only if identical across tracks */
        str = cddb_disc_get_artist(sys->cddb);
        if (NONEMPTY(str))
429
        {
430
            for (int i = 0; i < sys->titles; i++)
431
            {
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
                cddb_track_t *t = cddb_disc_get_track(sys->cddb, i);
                if (t == NULL)
                    continue;

                const char *track_artist = cddb_track_get_artist(t);
                if (NONEMPTY(track_artist))
                {
                    if (str == NULL)
                        str = track_artist;
                    else
                    if (strcmp(str, track_artist))
                    {
                        str = NULL;
                        break;
                    }
                }
448 449
            }
        }
450
    }
451
#endif
452
}
453

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
454
static int ReadDir(stream_t *access, input_item_node_t *node)
455 456
{
    access_sys_t *sys = access->p_sys;
457

458
    /* Build title table */
459
    for (int i = 0; i < sys->titles; i++)
460
    {
461
        msg_Dbg(access, "track[%d] start=%d", i, sys->p_sectors[i]);
462

463 464
        /* Initial/default name */
        char *name;
465

466 467
        if (unlikely(asprintf(&name, _("Audio CD - Track %02i"), i + 1) == -1))
            name = NULL;
468 469

        /* Create playlist items */
470 471 472
        const mtime_t duration =
            (mtime_t)(sys->p_sectors[i + 1] - sys->p_sectors[i])
            * CDDA_DATA_SIZE * CLOCK_FREQ / 44100 / 2 / 2;
473

474 475 476 477
        input_item_t *item = input_item_NewDisc(access->psz_url,
                                                (name != NULL) ? name :
                                                access->psz_url, duration);
        free(name);
478

479
        if (unlikely(item == NULL))
480 481
            continue;

482 483
        char *opt;
        if (likely(asprintf(&opt, "cdda-track=%i", i + 1) != -1))
484
        {
485 486
            input_item_AddOption(item, opt, VLC_INPUT_OPTION_TRUSTED);
            free(opt);
487
        }
488 489 490

        if (likely(asprintf(&opt, "cdda-first-sector=%i",
                            sys->p_sectors[i]) != -1))
491
        {
492 493
            input_item_AddOption(item, opt, VLC_INPUT_OPTION_TRUSTED);
            free(opt);
494
        }
495 496 497

        if (likely(asprintf(&opt, "cdda-last-sector=%i",
                            sys->p_sectors[i + 1]) != -1))
498
        {
499 500
            input_item_AddOption(item, opt, VLC_INPUT_OPTION_TRUSTED);
            free(opt);
501
        }
502

503 504 505 506 507 508
        const char *title = NULL;
        const char *artist = NULL;
        const char *album = NULL;
        const char *genre = NULL;
        const char *description = NULL;
        int year = 0;
509

510
#ifdef HAVE_LIBCDDB
511
        if (sys->cddb != NULL)
512
        {
513 514
            cddb_track_t *t = cddb_disc_get_track(sys->cddb, i);
            if (t != NULL)
515
            {
516 517
                title = cddb_track_get_title(t);
                artist = cddb_track_get_artist(t);
518
            }
519 520 521 522 523

            ON_EMPTY(artist, cddb_disc_get_artist(sys->cddb));
            album = cddb_disc_get_title(sys->cddb);
            genre = cddb_disc_get_genre(sys->cddb);
            year = cddb_disc_get_year(sys->cddb);
524 525
        }
#endif
526
        const vlc_meta_t *m;
527

528
        if (sys->cdtextc > 0 && (m = sys->cdtextv[0]) != NULL)
529
        {
530 531 532 533
            ON_EMPTY(artist, vlc_meta_Get(m, vlc_meta_Artist));
            ON_EMPTY(album,  vlc_meta_Get(m, vlc_meta_Album));
            ON_EMPTY(genre,  vlc_meta_Get(m, vlc_meta_Genre));
            description =    vlc_meta_Get(m, vlc_meta_Description);
534 535
        }

536 537 538 539 540 541 542
        if (i + 1 < sys->cdtextc && (m = sys->cdtextv[i + 1]) != NULL)
        {
            ON_EMPTY(title,       vlc_meta_Get(m, vlc_meta_Title));
            ON_EMPTY(artist,      vlc_meta_Get(m, vlc_meta_Artist));
            ON_EMPTY(genre,       vlc_meta_Get(m, vlc_meta_Genre));
            ON_EMPTY(description, vlc_meta_Get(m, vlc_meta_Description));
        }
543

544
        if (NONEMPTY(title))
545
        {
546 547
            input_item_SetName(item, title);
            input_item_SetTitle(item, title);
548
        }
549

550 551 552 553 554
        if (NONEMPTY(artist))
            input_item_SetArtist(item, artist);

        if (NONEMPTY(genre))
            input_item_SetGenre(item, genre);
555

556 557
        if (NONEMPTY(description))
            input_item_SetDescription(item, description);
558

559 560
        if (NONEMPTY(album))
            input_item_SetAlbum(item, album);
561

562 563 564
        if (year != 0)
        {
            char yearbuf[5];
565

566 567 568
            snprintf(yearbuf, sizeof (yearbuf), "%u", year);
            input_item_SetDate(item, yearbuf);
        }
569

570 571 572
        char num[4];
        snprintf(num, sizeof (num), "%d", i + 1);
        input_item_SetTrackNum(item, num);
573

574 575
        input_item_node_AppendItem(node, item);
        input_item_Release(item);
576
    }
577 578
#undef ON_EMPTY
#undef NONEMPTY
579
    return VLC_SUCCESS;
580 581
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
582
static int AccessControl(stream_t *access, int query, va_list args)
583 584 585 586 587 588 589 590 591
{
    if (query == STREAM_GET_META)
    {
        AccessGetMeta(access, va_arg(args, vlc_meta_t *));
        return VLC_SUCCESS;
    }
    return access_vaDirectoryControlHelper(access, query, args);
}

592
static int AccessOpen(vlc_object_t *obj, vcddev_t *dev)
593
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
594
    stream_t *access = (stream_t *)obj;
595
    /* Only whole discs here */
596
    access_sys_t *sys = vlc_obj_malloc(obj, sizeof (*sys));
597
    if (unlikely(sys == NULL))
598
    {
599 600
        ioctl_Close(obj, dev);
        return VLC_ENOMEM;
601
    }
602 603

    sys->vcddev = dev;
604
    sys->p_sectors = NULL;
605

606
    sys->titles = ioctl_GetTracksMap(obj, dev, &sys->p_sectors);
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
    if (sys->titles < 0)
    {
        msg_Err(obj, "cannot count tracks");
        goto error;
    }

    if (sys->titles == 0)
    {
        msg_Err(obj, "no audio tracks found");
        goto error;
    }

#ifdef HAVE_LIBCDDB
    msg_Dbg(obj, "retrieving metadata with CDDB");

    sys->cddb = GetCDDBInfo(obj, sys->titles, sys->p_sectors);
    if (sys->cddb != NULL)
        msg_Dbg(obj, "disc ID: 0x%08x", cddb_disc_get_discid(sys->cddb));
    else
        msg_Dbg(obj, "CDDB failure");
#endif

629
    if (ioctl_GetCdText(obj, dev, &sys->cdtextv, &sys->cdtextc))
630 631 632 633 634 635
    {
        msg_Dbg(obj, "CD-TEXT information missing");
        sys->cdtextv = NULL;
        sys->cdtextc = 0;
    }

636 637 638 639 640 641
    access->p_sys = sys;
    access->pf_read = NULL;
    access->pf_block = NULL;
    access->pf_readdir = ReadDir;
    access->pf_seek = NULL;
    access->pf_control = AccessControl;
642
    return VLC_SUCCESS;
643

644
error:
645
    free(sys->p_sectors);
646
    ioctl_Close(obj, dev);
647 648
    return VLC_EGENERIC;
}
649

650
static void AccessClose(access_sys_t *sys)
651
{
652 653 654 655 656 657 658 659 660 661 662 663 664
    for (int i = 0; i < sys->cdtextc; i++)
    {
        vlc_meta_t *meta = sys->cdtextv[i];
        if (meta != NULL)
            vlc_meta_Delete(meta);
    }
    free(sys->cdtextv);

#ifdef HAVE_LIBCDDB
    if (sys->cddb != NULL)
        cddb_disc_destroy(sys->cddb);
#endif

665
    free(sys->p_sectors);
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
}

static int Open(vlc_object_t *obj)
{
    stream_t *stream = (stream_t *)obj;
    unsigned track;

    vcddev_t *dev = DiscOpen(obj, stream->psz_location, stream->psz_filepath,
                             &track);
    if (dev == NULL)
        return VLC_EGENERIC;

    if (track == 0)
        return AccessOpen(obj, dev);
    else
        return DemuxOpen(obj, dev, track);
}

static void Close(vlc_object_t *obj)
{
    stream_t *stream = (stream_t *)obj;
    void *sys = stream->p_sys;

    if (stream->pf_readdir != NULL)
        AccessClose(sys);

    static_assert(offsetof(demux_sys_t, vcddev) == 0, "Invalid cast");
    static_assert(offsetof(access_sys_t, vcddev) == 0, "Invalid cast");
    ioctl_Close(obj, *(vcddev_t **)sys);
695
}
696

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
/*****************************************************************************
 * Module descriptior
 *****************************************************************************/
#define CDAUDIO_DEV_TEXT N_("Audio CD device")
#if defined( _WIN32 ) || defined( __OS2__ )
# define CDAUDIO_DEV_LONGTEXT N_( \
    "This is the default Audio CD drive (or file) to use. Don't forget the " \
    "colon after the drive letter (e.g. D:)")
# define CD_DEVICE      "D:"
#else
# define CDAUDIO_DEV_LONGTEXT N_( \
    "This is the default Audio CD device to use." )
# if defined(__OpenBSD__)
#  define CD_DEVICE      "/dev/cd0c"
# elif defined(__linux__)
#  define CD_DEVICE      "/dev/sr0"
# else
#  define CD_DEVICE      "/dev/cdrom"
# endif
#endif
717

718 719 720
vlc_module_begin ()
    set_shortname( N_("Audio CD") )
    set_description( N_("Audio CD input") )
721
    set_capability( "access", 0 )
722 723
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_ACCESS )
724
    set_callbacks(Open, Close)
725

726
    add_loadfile("cd-audio", CD_DEVICE, CDAUDIO_DEV_TEXT, CDAUDIO_DEV_LONGTEXT)
727

728 729 730 731 732 733 734
    add_usage_hint( N_("[cdda:][device][@[track]]") )
    add_integer( "cdda-track", 0 , NULL, NULL, true )
        change_volatile ()
    add_integer( "cdda-first-sector", -1, NULL, NULL, true )
        change_volatile ()
    add_integer( "cdda-last-sector", -1, NULL, NULL, true )
        change_volatile ()
735

736 737 738 739 740 741 742
#ifdef HAVE_LIBCDDB
    add_string( "cddb-server", "freedb.videolan.org", N_( "CDDB Server" ),
            N_( "Address of the CDDB server to use." ), true )
    add_integer( "cddb-port", 80, N_( "CDDB port" ),
            N_( "CDDB Server port to use." ), true )
        change_integer_range( 1, 65535 )
#endif
743

744 745
    add_shortcut( "cdda", "cddasimple" )
vlc_module_end ()