dvd_input.c 9.86 KB
Newer Older
1 2
/*
 * Copyright (C) 2002 Samuel Hocevar <sam@zoy.org>,
3
 *                    Håkan Hjort <d95hjort@dtek.chalmers.se>
4
 *
5 6 7
 * This file is part of libdvdread.
 *
 * libdvdread is free software; you can redistribute it and/or modify
8 9 10
 * 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.
Erik Hovland's avatar
Erik Hovland committed
11
 *
12
 * libdvdread is distributed in the hope that it will be useful,
13 14 15 16
 * 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.
 *
17 18 19
 * You should have received a copy of the GNU General Public License along
 * with libdvdread; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 21
 */

Marcel Mol's avatar
Marcel Mol committed
22
#include "config.h"                  /* Required for HAVE_DVDCSS_DVDCSS_H */
23 24 25 26
#include <stdio.h>                               /* fprintf */
#include <stdlib.h>                              /* free */
#include <fcntl.h>                               /* open */
#include <unistd.h>                              /* lseek */
27

28
#include "dvdread/dvd_reader.h"      /* DVD_VIDEO_LB_LEN */
29 30 31 32
#include "dvd_input.h"


/* The function pointers that is the exported interface of this file. */
Thomas Guillem's avatar
Thomas Guillem committed
33
dvd_input_t (*dvdinput_open)  (const char *, void *, dvd_reader_stream_cb *);
34 35
int         (*dvdinput_close) (dvd_input_t);
int         (*dvdinput_seek)  (dvd_input_t, int);
Erik Hovland's avatar
Erik Hovland committed
36
int         (*dvdinput_title) (dvd_input_t, int);
37 38 39 40 41
int         (*dvdinput_read)  (dvd_input_t, void *, int, int);
char *      (*dvdinput_error) (dvd_input_t);

#ifdef HAVE_DVDCSS_DVDCSS_H
/* linking to libdvdcss */
42
# include <dvdcss/dvdcss.h>
Thomas Guillem's avatar
Thomas Guillem committed
43 44
# define DVDcss_open_stream(a, b) \
    dvdcss_open_stream((void*)(a), (dvdcss_stream_cb*)(b))
45 46 47 48 49
# define DVDcss_open(a) dvdcss_open((char*)(a))
# define DVDcss_close   dvdcss_close
# define DVDcss_seek    dvdcss_seek
# define DVDcss_read    dvdcss_read
# define DVDcss_error   dvdcss_error
50 51 52
#else

/* dlopening libdvdcss */
53 54 55 56
# if defined(HAVE_DLFCN_H) && !defined(USING_BUILTIN_DLFCN)
#  include <dlfcn.h>
# else
# if defined(WIN32)
57
/* Only needed on MINGW at the moment */
58 59
#  include "../msvc/contrib/dlfcn.c"
# endif
60 61
#endif

62
typedef struct dvdcss_s *dvdcss_t;
Thomas Guillem's avatar
Thomas Guillem committed
63 64
typedef struct dvdcss_stream_cb dvdcss_stream_cb;
static dvdcss_t (*DVDcss_open_stream) (void *, dvdcss_stream_cb *);
65 66 67 68 69
static dvdcss_t (*DVDcss_open)  (const char *);
static int      (*DVDcss_close) (dvdcss_t);
static int      (*DVDcss_seek)  (dvdcss_t, int, int);
static int      (*DVDcss_read)  (dvdcss_t, void *, int, int);
static char *   (*DVDcss_error) (dvdcss_t);
70
#define DVDCSS_SEEK_KEY (1 << 1)
71 72 73 74 75
#endif

/* The DVDinput handle, add stuff here for new input methods. */
struct dvd_input_s {
  /* libdvdcss handle */
76
  dvdcss_t dvdcss;
Erik Hovland's avatar
Erik Hovland committed
77

78 79 80 81 82 83
  /* dummy file input */
  int fd;
};


/**
Thomas Guillem's avatar
Thomas Guillem committed
84
 * initialize and open a DVD (device or file or stream_cb)
85
 */
Thomas Guillem's avatar
Thomas Guillem committed
86 87
static dvd_input_t css_open(const char *target,
                            void *stream, dvd_reader_stream_cb *stream_cb)
88 89
{
  dvd_input_t dev;
Erik Hovland's avatar
Erik Hovland committed
90

91
  /* Allocate the handle structure */
92
  dev = malloc(sizeof(*dev));
93 94 95 96
  if(dev == NULL) {
    fprintf(stderr, "libdvdread: Could not allocate memory.\n");
    return NULL;
  }
Erik Hovland's avatar
Erik Hovland committed
97

98
  /* Really open it with libdvdcss */
Thomas Guillem's avatar
Thomas Guillem committed
99 100
  if(target)
      dev->dvdcss = DVDcss_open(target);
101
  else if(stream && stream_cb) {
102
#ifdef HAVE_DVDCSS_DVDCSS_H
Thomas Guillem's avatar
Thomas Guillem committed
103
      dev->dvdcss = DVDcss_open_stream(stream, (dvdcss_stream_cb *)stream_cb);
104 105 106 107
#endif
      dev->dvdcss = DVDcss_open_stream ?
                    DVDcss_open_stream(stream, (dvdcss_stream_cb *)stream_cb) :
                    NULL;
108
  }
109 110 111 112 113
  if(dev->dvdcss == 0) {
    fprintf(stderr, "libdvdread: Could not open %s with libdvdcss.\n", target);
    free(dev);
    return NULL;
  }
Erik Hovland's avatar
Erik Hovland committed
114

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
  return dev;
}

/**
 * return the last error message
 */
static char *css_error(dvd_input_t dev)
{
  return DVDcss_error(dev->dvdcss);
}

/**
 * seek into the device.
 */
static int css_seek(dvd_input_t dev, int blocks)
{
  /* DVDINPUT_NOFLAGS should match the DVDCSS_NOFLAGS value. */
  return DVDcss_seek(dev->dvdcss, blocks, DVDINPUT_NOFLAGS);
}

/**
136
 * set the block for the beginning of a new title (key).
137 138 139
 */
static int css_title(dvd_input_t dev, int block)
{
140
  return DVDcss_seek(dev->dvdcss, block, DVDCSS_SEEK_KEY);
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
}

/**
 * read data from the device.
 */
static int css_read(dvd_input_t dev, void *buffer, int blocks, int flags)
{
  return DVDcss_read(dev->dvdcss, buffer, blocks, flags);
}

/**
 * close the DVD device and clean up the library.
 */
static int css_close(dvd_input_t dev)
{
  int ret;

  ret = DVDcss_close(dev->dvdcss);

  if(ret < 0)
    return ret;

  free(dev);

  return 0;
}

/**
 * initialize and open a DVD device or file.
 */
Thomas Guillem's avatar
Thomas Guillem committed
171
static dvd_input_t file_open(const char *target,
172 173
                             void *stream UNUSED,
                             dvd_reader_stream_cb *stream_cb UNUSED)
174 175
{
  dvd_input_t dev;
Erik Hovland's avatar
Erik Hovland committed
176

Thomas Guillem's avatar
Thomas Guillem committed
177 178
  if(target == NULL)
    return NULL;
179
  /* Allocate the library structure */
180
  dev = malloc(sizeof(*dev));
181 182 183 184
  if(dev == NULL) {
    fprintf(stderr, "libdvdread: Could not allocate memory.\n");
    return NULL;
  }
Erik Hovland's avatar
Erik Hovland committed
185

186
  /* Open the device */
187
#if !defined(WIN32) && !defined(__OS2__)
188 189 190 191 192 193 194 195 196
  dev->fd = open(target, O_RDONLY);
#else
  dev->fd = open(target, O_RDONLY | O_BINARY);
#endif
  if(dev->fd < 0) {
    perror("libdvdread: Could not open input");
    free(dev);
    return NULL;
  }
Erik Hovland's avatar
Erik Hovland committed
197

198 199 200 201 202 203
  return dev;
}

/**
 * return the last error message
 */
204
static char *file_error(dvd_input_t dev UNUSED)
205 206 207 208 209 210 211 212 213 214 215 216 217 218
{
  /* use strerror(errno)? */
  return (char *)"unknown error";
}

/**
 * seek into the device.
 */
static int file_seek(dvd_input_t dev, int blocks)
{
  off_t pos;

  pos = lseek(dev->fd, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN, SEEK_SET);
  if(pos < 0) {
219
    return pos;
220 221 222 223 224 225
  }
  /* assert pos % DVD_VIDEO_LB_LEN == 0 */
  return (int) (pos / DVD_VIDEO_LB_LEN);
}

/**
226
 * set the block for the beginning of a new title (key).
227
 */
228
static int file_title(dvd_input_t dev UNUSED, int block UNUSED)
229 230 231 232 233 234 235
{
  return -1;
}

/**
 * read data from the device.
 */
236 237
static int file_read(dvd_input_t dev, void *buffer, int blocks,
		     int flags UNUSED)
238
{
239
  size_t len, bytes;
Erik Hovland's avatar
Erik Hovland committed
240

241
  len = (size_t)blocks * DVD_VIDEO_LB_LEN;
242
  bytes = 0;
Erik Hovland's avatar
Erik Hovland committed
243

244
  while(len > 0) {
245
    ssize_t ret = read(dev->fd, ((char*)buffer) + bytes, len);
Erik Hovland's avatar
Erik Hovland committed
246

247 248
    if(ret < 0) {
      /* One of the reads failed, too bad.  We won't even bother
249 250
       * returning the reads that went OK, and as in the POSIX spec
       * the file position is left unspecified after a failure. */
251 252
      return ret;
    }
Erik Hovland's avatar
Erik Hovland committed
253

254
    if(ret == 0) {
255 256
      /* Nothing more to read.  Return all of the whole blocks, if any.
       * Adjust the file position back to the previous block boundary. */
257
      off_t over_read = -(bytes % DVD_VIDEO_LB_LEN);
258 259 260
      off_t pos = lseek(dev->fd, over_read, SEEK_CUR);
      if(pos % 2048 != 0)
        fprintf( stderr, "libdvdread: lseek not multiple of 2048! Something is wrong!\n" );
261 262
      return (int) (bytes / DVD_VIDEO_LB_LEN);
    }
Erik Hovland's avatar
Erik Hovland committed
263

264
    len -= ret;
265
    bytes += ret;
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
  }

  return blocks;
}

/**
 * close the DVD device and clean up.
 */
static int file_close(dvd_input_t dev)
{
  int ret;

  ret = close(dev->fd);

  if(ret < 0)
    return ret;

  free(dev);

  return 0;
}


/**
 * Setup read functions with either libdvdcss or minimal DVD access.
 */
int dvdinput_setup(void)
{
  void *dvdcss_library = NULL;

#ifdef HAVE_DVDCSS_DVDCSS_H
  /* linking to libdvdcss */
  dvdcss_library = &dvdcss_library;  /* Give it some value != NULL */

#else
  /* dlopening libdvdcss */

#ifdef __APPLE__
  #define CSS_LIB "libdvdcss.2.dylib"
#elif defined(WIN32)
306
  #define CSS_LIB "libdvdcss-2.dll"
komh's avatar
komh committed
307
#elif defined(__OS2__)
308
  #define CSS_LIB "dvdcss2.dll"
309 310 311 312 313 314
#else
  #define CSS_LIB "libdvdcss.so.2"
#endif
  dvdcss_library = dlopen(CSS_LIB, RTLD_LAZY);

  if(dvdcss_library != NULL) {
komh's avatar
komh committed
315
#if defined(__OpenBSD__) && !defined(__ELF__) || defined(__OS2__)
316 317 318 319
#define U_S "_"
#else
#define U_S
#endif
Thomas Guillem's avatar
Thomas Guillem committed
320 321
    DVDcss_open_stream = (dvdcss_t (*)(void *, dvdcss_stream_cb *))
      dlsym(dvdcss_library, U_S "dvdcss_open_stream");
322
    DVDcss_open = (dvdcss_t (*)(const char*))
323
      dlsym(dvdcss_library, U_S "dvdcss_open");
324
    DVDcss_close = (int (*)(dvdcss_t))
325
      dlsym(dvdcss_library, U_S "dvdcss_close");
326
    DVDcss_seek = (int (*)(dvdcss_t, int, int))
327
      dlsym(dvdcss_library, U_S "dvdcss_seek");
328
    DVDcss_read = (int (*)(dvdcss_t, void*, int, int))
329
      dlsym(dvdcss_library, U_S "dvdcss_read");
330
    DVDcss_error = (char* (*)(dvdcss_t))
331
      dlsym(dvdcss_library, U_S "dvdcss_error");
Erik Hovland's avatar
Erik Hovland committed
332

333
    if(dlsym(dvdcss_library, U_S "dvdcss_crack")) {
Erik Hovland's avatar
Erik Hovland committed
334
      fprintf(stderr,
335 336 337
              "libdvdread: Old (pre-0.0.2) version of libdvdcss found.\n"
              "libdvdread: You should get the latest version from "
              "http://www.videolan.org/\n" );
338 339
      dlclose(dvdcss_library);
      dvdcss_library = NULL;
340
    } else if(!DVDcss_open || !DVDcss_close || !DVDcss_seek
341
              || !DVDcss_read || !DVDcss_error) {
342
      fprintf(stderr,  "libdvdread: Missing symbols in %s, "
343
              "this shouldn't happen !\n", CSS_LIB);
344
      dlclose(dvdcss_library);
345
      dvdcss_library = NULL;
346 347 348
    }
  }
#endif /* HAVE_DVDCSS_DVDCSS_H */
Erik Hovland's avatar
Erik Hovland committed
349

350 351 352 353 354 355 356
  if(dvdcss_library != NULL) {
    /*
    char *psz_method = getenv( "DVDCSS_METHOD" );
    char *psz_verbose = getenv( "DVDCSS_VERBOSE" );
    fprintf(stderr, "DVDCSS_METHOD %s\n", psz_method);
    fprintf(stderr, "DVDCSS_VERBOSE %s\n", psz_verbose);
    */
Erik Hovland's avatar
Erik Hovland committed
357

358 359 360 361 362 363 364 365
    /* libdvdcss wrapper functions */
    dvdinput_open  = css_open;
    dvdinput_close = css_close;
    dvdinput_seek  = css_seek;
    dvdinput_title = css_title;
    dvdinput_read  = css_read;
    dvdinput_error = css_error;
    return 1;
Erik Hovland's avatar
Erik Hovland committed
366

367 368 369 370 371 372 373 374 375 376 377 378 379
  } else {
    fprintf(stderr, "libdvdread: Encrypted DVD support unavailable.\n");

    /* libdvdcss replacement functions */
    dvdinput_open  = file_open;
    dvdinput_close = file_close;
    dvdinput_seek  = file_seek;
    dvdinput_title = file_title;
    dvdinput_read  = file_read;
    dvdinput_error = file_error;
    return 0;
  }
}