summaryrefslogtreecommitdiffstats
path: root/libdvdread-embedded/src/dvd_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdvdread-embedded/src/dvd_input.c')
-rw-r--r--libdvdread-embedded/src/dvd_input.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/libdvdread-embedded/src/dvd_input.c b/libdvdread-embedded/src/dvd_input.c
new file mode 100644
index 0000000..26b6928
--- /dev/null
+++ b/libdvdread-embedded/src/dvd_input.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2002 Samuel Hocevar <sam@zoy.org>,
+ * HÃ¥kan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread 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.
+ *
+ * libdvdread 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 libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h" /* Required for HAVE_DVDCSS_DVDCSS_H */
+#include <stdio.h> /* fprintf */
+#include <stdlib.h> /* free */
+#include <fcntl.h> /* open */
+#include <unistd.h> /* lseek */
+#include <string.h> /* strerror */
+#include <errno.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include "../msvc/contrib/win32_cs.h"
+#endif
+
+#include "dvdread/dvd_reader.h" /* DVD_VIDEO_LB_LEN */
+#include "dvdread_internal.h"
+#include "dvd_input.h"
+#include "logger.h"
+
+
+/* The function pointers that is the exported interface of this file. */
+dvd_input_t (*dvdinput_open) (void *, dvd_logger_cb *,
+ const char *,dvd_reader_stream_cb *);
+int (*dvdinput_close) (dvd_input_t);
+int (*dvdinput_seek) (dvd_input_t, int);
+int (*dvdinput_title) (dvd_input_t, int);
+int (*dvdinput_read) (dvd_input_t, void *, int, int);
+
+#ifdef HAVE_DVDCSS_DVDCSS_H
+/* linking to libdvdcss */
+# include <dvdcss/dvdcss.h>
+# define DVDcss_open_stream(a, b) \
+ dvdcss_open_stream((void*)(a), (dvdcss_stream_cb*)(b))
+# define DVDcss_open(a) dvdcss_open((char*)(a))
+# define DVDcss_close dvdcss_close
+# define DVDcss_seek dvdcss_seek
+# define DVDcss_read dvdcss_read
+#else
+
+/* dlopening libdvdcss */
+# if defined(HAVE_DLFCN_H) && !defined(USING_BUILTIN_DLFCN)
+# include <dlfcn.h>
+# else
+# if defined(_WIN32)
+/* Only needed on MINGW at the moment */
+# include "../msvc/contrib/dlfcn.c"
+# endif
+# endif
+
+typedef struct dvdcss_s *dvdcss_t;
+typedef struct dvdcss_stream_cb dvdcss_stream_cb;
+static dvdcss_t (*DVDcss_open_stream) (void *, dvdcss_stream_cb *);
+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);
+#define DVDCSS_SEEK_KEY (1 << 1)
+#endif
+
+#ifdef _WIN32
+static int open_win32(const char *path, int flags)
+{
+ wchar_t *wpath;
+ int fd;
+
+ wpath = _utf8_to_wchar(path);
+ if (!wpath) {
+ return -1;
+ }
+ fd = _wopen(wpath, flags);
+ free(wpath);
+ return fd;
+}
+#endif
+
+/* The DVDinput handle, add stuff here for new input methods.
+ * NOTE: All members of this structure must be initialized in dvd_input_New
+ */
+struct dvd_input_s {
+ /* libdvdcss handle */
+ dvdcss_t dvdcss;
+ /* */
+ void *priv;
+ dvd_logger_cb *logcb;
+ off_t ipos;
+
+ /* dummy file input */
+ int fd;
+ /* stream input */
+ dvd_reader_stream_cb *stream_cb;
+};
+
+static dvd_input_t dvd_input_New(void *priv, dvd_logger_cb *logcb)
+{
+ dvd_input_t dev = calloc(1, sizeof(*dev));
+ if(dev)
+ {
+ dev->priv = priv;
+ dev->logcb = logcb;
+ dev->ipos = 0;
+
+ /* Initialize all inputs to safe defaults */
+ dev->dvdcss = NULL;
+ dev->fd = -1;
+ dev->stream_cb = NULL;
+ }
+ return dev;
+}
+
+/**
+ * initialize and open a DVD (device or file or stream_cb)
+ */
+static dvd_input_t css_open(void *priv, dvd_logger_cb *logcb,
+ const char *target,
+ dvd_reader_stream_cb *stream_cb)
+{
+ dvd_input_t dev;
+
+ /* Allocate the handle structure */
+ dev = dvd_input_New(priv, logcb);
+ if(dev == NULL) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Could not allocate memory.");
+ return NULL;
+ }
+
+ /* Really open it with libdvdcss */
+ if(target)
+ dev->dvdcss = DVDcss_open(target);
+ else if(priv && stream_cb) {
+#ifdef HAVE_DVDCSS_DVDCSS_H
+ dev->dvdcss = DVDcss_open_stream(priv, (dvdcss_stream_cb *)stream_cb);
+#else
+ dev->dvdcss = DVDcss_open_stream ?
+ DVDcss_open_stream(priv, (dvdcss_stream_cb *)stream_cb) :
+ NULL;
+#endif
+ }
+ if(dev->dvdcss == NULL) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Could not open %s with libdvdcss.", target);
+ free(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+/**
+ * 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);
+}
+
+/**
+ * set the block for the beginning of a new title (key).
+ */
+static int css_title(dvd_input_t dev, int block)
+{
+ return DVDcss_seek(dev->dvdcss, block, DVDCSS_SEEK_KEY);
+}
+
+/**
+ * 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);
+
+ free(dev);
+
+ return ret;
+}
+
+/**
+ * initialize and open a DVD device or file.
+ */
+static dvd_input_t file_open(void *priv, dvd_logger_cb *logcb,
+ const char *target,
+ dvd_reader_stream_cb *stream_cb)
+{
+ dvd_input_t dev;
+
+ /* Allocate the library structure */
+ dev = dvd_input_New(priv, logcb);
+ if(dev == NULL) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Could not allocate memory.");
+ return NULL;
+ }
+
+ /* Initialize with stream callback if it is specified */
+ if (stream_cb) {
+ if (!stream_cb->pf_read || !stream_cb->pf_seek) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Stream callback provided but lacks of pf_read or pf_seek methods.");
+ free(dev);
+ return NULL;
+ }
+ dev->stream_cb = stream_cb;
+ return dev;
+ }
+
+ /* Open the device */
+ if(target == NULL) {
+ free(dev);
+ return NULL;
+ }
+#if defined(_WIN32)
+ dev->fd = open_win32(target, O_RDONLY | O_BINARY);
+#elif defined(__OS2__)
+ dev->fd = open(target, O_RDONLY | O_BINARY);
+#else
+ dev->fd = open(target, O_RDONLY);
+#endif
+ if(dev->fd < 0) {
+ char buf[256];
+#if defined(HAVE_STRERROR_R) && defined(HAVE_DECL_STRERROR_R)
+ #ifdef STRERROR_R_CHAR_P
+ *buf=0;
+ if(strerror_r(errno, buf, 256) == NULL)
+ *buf=0;
+ #else
+ if(strerror_r(errno, buf, 256) != 0)
+ *buf=0;
+ #endif
+#else
+ #if defined(HAVE_STRERR_S)
+ if(strerror_s(buf, 256, errno) != 0)
+ *buf=0;
+ #else
+ *buf=0;
+ #endif
+#endif
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Could not open input: %s", buf);
+ free(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+/**
+ * seek into the device.
+ */
+static int file_seek(dvd_input_t dev, int blocks)
+{
+ off_t pos = -1;
+
+ if(dev->ipos == blocks)
+ {
+ /* We are already in position */
+ return blocks;
+ }
+
+ if (dev->stream_cb) {
+ /* Returns 0 on successful completion and -1 on error */
+ pos = dev->stream_cb->pf_seek(dev->priv, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN);
+
+ if (!pos) {
+ dev->ipos = blocks;
+ }
+ } else {
+ /* Returns position as the number of bytes from beginning of file
+ * or -1 on error
+ */
+ pos = lseek(dev->fd, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN, SEEK_SET);
+
+ if (pos >= 0) {
+ dev->ipos = pos / DVD_VIDEO_LB_LEN;
+ }
+ }
+
+ if(pos < 0) {
+ return pos;
+ }
+
+ /* assert pos % DVD_VIDEO_LB_LEN == 0 */
+ return (int) dev->ipos;
+}
+
+/**
+ * set the block for the beginning of a new title (key).
+ */
+static int file_title(dvd_input_t dev UNUSED, int block UNUSED)
+{
+ return -1;
+}
+
+/**
+ * read data from the device.
+ */
+static int file_read(dvd_input_t dev, void *buffer, int blocks,
+ int flags UNUSED)
+{
+ size_t len, bytes, blocks_read;
+
+ len = (size_t)blocks * DVD_VIDEO_LB_LEN;
+ bytes = 0;
+ blocks_read = 0;
+
+ while(len > 0) {
+ ssize_t ret = -1;
+
+ /* Perform read based on the input type */
+ if (dev->stream_cb) {
+ /* Returns the number of bytes read or -1 on error */
+ ret = dev->stream_cb->pf_read(dev->priv, ((char*)buffer) + bytes, len);
+ } else {
+ /* Returns the number of bytes read or -1 on error */
+ ret = read(dev->fd, ((char*)buffer) + bytes, len);
+ }
+
+ if(ret < 0) {
+ /* One of the reads failed, too bad. We won't even bother
+ * returning the reads that went OK, and as in the POSIX spec
+ * the file position is left unspecified after a failure. */
+ dev->ipos = -1;
+ return ret;
+ }
+
+ if(ret == 0) {
+ /* Nothing more to read. Return all of the whole blocks, if any.
+ * Adjust the file position back to the previous block boundary. */
+ ret = file_seek(dev, dev->ipos + blocks_read);
+ if(ret < 0)
+ return ret;
+
+ return (int) blocks_read;
+ }
+
+ len -= ret;
+ bytes += ret;
+ blocks_read = bytes / DVD_VIDEO_LB_LEN;
+ }
+
+ dev->ipos += blocks_read;
+ return blocks;
+}
+
+/**
+ * close the DVD device and clean up.
+ */
+static int file_close(dvd_input_t dev)
+{
+ int ret = 0;
+
+ /* close file if it was open */
+
+ if (dev->fd >= 0) {
+ ret = close(dev->fd);
+ }
+
+ free(dev);
+
+ return ret;
+}
+
+/**
+ * Setup read functions with either libdvdcss or minimal DVD access.
+ */
+int dvdinput_setup(void *priv, dvd_logger_cb *logcb)
+{
+ 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)
+ #define CSS_LIB "libdvdcss-2.dll"
+#elif defined(__OS2__)
+ #define CSS_LIB "dvdcss2.dll"
+#elif defined(__OpenBSD__)
+ #define CSS_LIB "libdvdcss.so"
+#else
+ #define CSS_LIB "libdvdcss.so.2"
+#endif
+ dvdcss_library = dlopen(CSS_LIB, RTLD_LAZY);
+
+ if(dvdcss_library != NULL) {
+#ifdef __OS2__
+#define U_S "_"
+#else
+#define U_S
+#endif
+ DVDcss_open_stream = (dvdcss_t (*)(void *, dvdcss_stream_cb *))
+ dlsym(dvdcss_library, U_S "dvdcss_open_stream");
+ DVDcss_open = (dvdcss_t (*)(const char*))
+ dlsym(dvdcss_library, U_S "dvdcss_open");
+ DVDcss_close = (int (*)(dvdcss_t))
+ dlsym(dvdcss_library, U_S "dvdcss_close");
+ DVDcss_seek = (int (*)(dvdcss_t, int, int))
+ dlsym(dvdcss_library, U_S "dvdcss_seek");
+ DVDcss_read = (int (*)(dvdcss_t, void*, int, int))
+ dlsym(dvdcss_library, U_S "dvdcss_read");
+
+ if(dlsym(dvdcss_library, U_S "dvdcss_crack")) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Old (pre-0.0.2) version of libdvdcss found. "
+ "libdvdread: You should get the latest version from "
+ "http://www.videolan.org/" );
+ dlclose(dvdcss_library);
+ dvdcss_library = NULL;
+ } else if(!DVDcss_open || !DVDcss_close || !DVDcss_seek
+ || !DVDcss_read) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Missing symbols in %s, "
+ "this shouldn't happen !", CSS_LIB);
+ dlclose(dvdcss_library);
+ dvdcss_library = NULL;
+ }
+ }
+#endif /* HAVE_DVDCSS_DVDCSS_H */
+
+ 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);
+ */
+
+ /* libdvdcss wrapper functions */
+ dvdinput_open = css_open;
+ dvdinput_close = css_close;
+ dvdinput_seek = css_seek;
+ dvdinput_title = css_title;
+ dvdinput_read = css_read;
+ return 1;
+
+ } else {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_WARN,
+ "Encrypted DVD support unavailable.");
+
+ /* libdvdcss replacement functions */
+ dvdinput_open = file_open;
+ dvdinput_close = file_close;
+ dvdinput_seek = file_seek;
+ dvdinput_title = file_title;
+ dvdinput_read = file_read;
+ return 0;
+ }
+}