This patch enables dvdread to be able to read .ISO files inside of a RAR
archive. It does so by using popen() to spawn "unrar" executable. It is
recommended it is used with the "unrar" version with "-sk<offset>"
feature to allow to lseek() in the RAR archives.
Lund
--
Jorgen Lundman | <[EMAIL PROTECTED]>
Unix Administrator | +81 (0)3 -5456-2687 ext 1017 (work)
Shibuya-ku, Tokyo | +81 (0)90-5578-8500 (cell)
Japan | +81 (0)3 -3375-1767 (home)
diff -ruib ../../versions/libdvdread_patch4/libdvdread/src/dvd_input.c
./dvd_input.c
--- ../../versions/libdvdread_patch4/libdvdread/src/dvd_input.c 2008-10-10
14:42:23.000000000 +0900
+++ ./dvd_input.c 2008-10-10 17:40:50.000000000 +0900
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
+#include <string.h>
#include "dvd_reader.h"
#include "dvd_input.h"
@@ -37,6 +38,9 @@
int (*dvdinput_title) (dvd_input_t, int);
int (*dvdinput_read) (dvd_input_t, void *, int, int);
char * (*dvdinput_error) (dvd_input_t);
+char * dvdinput_unrar_cmd = NULL;
+char * dvdinput_unrar_file = NULL;
+
#ifdef HAVE_DVDCSS_DVDCSS_H
/* linking to libdvdcss */
@@ -66,6 +70,9 @@
static char * (*DVDcss_error) (dvdcss_handle);
#endif
+/* When is it faster to respawn unrar, rather than seek by eating bytes? */
+#define MAXIMUM_SEEK_SIZE 1048576
+
/* The DVDinput handle, add stuff here for new input methods. */
struct dvd_input_s {
/* libdvdcss handle */
@@ -73,6 +80,13 @@
/* dummy file input */
int fd;
+
+ /* unrar support */
+ char *unrar_archive;
+ char *unrar_file;
+ FILE *unrar_stream;
+ off_t seek_pos;
+ off_t current_pos;
};
@@ -364,3 +378,263 @@
return 0;
}
}
+
+/**
+ * initialize and open a DVD device or file in a RAR file.
+ */
+static dvd_input_t rarfile_open(const char *target)
+{
+ dvd_input_t dev;
+
+ /* Allocate the library structure */
+ dev = (dvd_input_t) malloc(sizeof(*dev));
+ if(dev == NULL) {
+ fprintf(stderr, "libdvdread: Could not allocate memory.\n");
+ return NULL;
+ }
+
+ dev->unrar_stream = NULL;
+ dev->unrar_file = NULL;
+ dev->seek_pos = 0; /* Assume start of file */
+ dev->current_pos = 0;
+
+ /* This is kind of sad, to use setupRAR to pass the rarfile name, then
+ * hope that dvdinput_open is called after. We could also do some sort
+ * of filename separator parsing perhaps. */
+
+ /* Allocated in setupRAR, move it over. */
+ dev->unrar_archive = dvdinput_unrar_file;
+ dvdinput_unrar_file = NULL;
+
+ /* The filename inside the RAR archive to operate on */
+ dev->unrar_file = strdup(target);
+
+ /* Lets call unrar, to list the release, as a way to quickly check that
+ * all the volumes are present, and the unrar_cmd works? */
+
+ return dev;
+}
+
+
+/**
+ * return the last error message
+ */
+static char *rarfile_error(dvd_input_t dev)
+{
+ /* use strerror(errno)? */
+ return (char *)"unknown error";
+}
+
+/**
+ * seek into the device.
+ */
+static int rarfile_seek(dvd_input_t dev, int blocks)
+{
+
+ dev->seek_pos = (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN;
+
+ /* assert pos % DVD_VIDEO_LB_LEN == 0 */
+ return (int) (dev->seek_pos / DVD_VIDEO_LB_LEN);
+}
+
+/**
+ * set the block for the begining of a new title (key).
+ */
+static int rarfile_title(dvd_input_t dev, int block)
+{
+ return -1;
+}
+
+/**
+ * read data from the device.
+ */
+static int rarfile_read(dvd_input_t dev, void *buffer, int blocks, int flags)
+{
+ size_t len, read_size;
+ char ibuffer[DVD_VIDEO_LB_LEN];
+ int ret;
+
+ /* First, position ourselves where the API wants us. This may mean
+ * spawning new unrar, or possibly eating data until the correct
+ * position. */
+
+ /* We already have a stream, here, we can be exactly at the right place,
+ * or, need to eat data until the correct position, or
+ * if the seek is too far, close this unrar, and spawn a new.
+ */
+ if (dev->unrar_stream) {
+
+ /* eat data? */
+ if (dev->seek_pos > dev->current_pos) {
+
+ /* Seek too far? Better to spawn new unrar? */
+ if ((dev->seek_pos - dev->current_pos) >= MAXIMUM_SEEK_SIZE) {
+#ifdef DEBUG
+ fprintf(stderr, "libdvdread: seek too large, re-spawning unrar\r\n");
+#endif
+ pclose(dev->unrar_stream);
+ dev->unrar_stream = NULL;
+
+ } else {
+ /* Not too far, read and eat bytes. */
+ while (dev->seek_pos > dev->current_pos) {
+
+ /* Work out how much we need to read, but no larger than
+ * the size of our buffer.*/
+
+ read_size = dev->seek_pos - dev->current_pos;
+ if (read_size > sizeof(ibuffer))
+ read_size = sizeof(ibuffer);
+
+ if ((fread(ibuffer, read_size, 1, dev->unrar_stream)) != 1) {
+ /* Something failed, lets close, and let's try respawn */
+ pclose(dev->unrar_stream);
+ dev->unrar_stream = NULL;
+ break;
+ }
+ dev->current_pos += read_size;
+ } /* while seek > current */
+ } /* NOT >= max seek */
+ } /* NOT seek > current */
+
+ /* Also check if seek < current, then we must restart unrar */
+ if (dev->seek_pos < dev->current_pos) {
+ pclose(dev->unrar_stream);
+ dev->unrar_stream = NULL;
+ }
+
+ } /* we have active stream */
+
+ /* Spawn new unrar? */
+ if (!dev->unrar_stream) {
+ snprintf(ibuffer, sizeof(ibuffer),
+ "%s p -inul -c- -p- -y -cfg- -sk%"PRIu64" -- \"%s\" \"%s\"",
+ dvdinput_unrar_cmd,
+ dev->seek_pos,
+ dev->unrar_archive,
+ dev->unrar_file );
+
+#ifdef DEBUG
+ fprintf(stderr, "libdvdvread: spawning '%s'\r\n", ibuffer);
+#endif
+
+ dev->unrar_stream = popen(ibuffer,
+#ifdef WIN32
+ "rb"
+#else
+ "r" /* It is an error to send "b" in Unix :( */
+#endif
+ );
+
+ if (!dev->unrar_stream) {
+ return -1;
+ }
+
+ /* Update ptr */
+ dev->current_pos = dev->seek_pos;
+
+ }
+
+ /* Assert current == seek ? */
+#ifdef DEBUG
+ if (dev->current_pos != dev->seek_pos)
+ fprintf(stderr, "libdvdread: somehow, current_pos != seek_pos!?\r\n");
+#endif
+
+ len = (size_t)blocks * DVD_VIDEO_LB_LEN;
+
+ while(len > 0) {
+
+ /* ret = read(dev->fd, buffer, len); */
+ ret = fread(buffer, 1, len, dev->unrar_stream);
+
+ if((ret != len) && ferror(dev->unrar_stream)) {
+ /* 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 postition is left unspecified after a failure. */
+ return -1;
+ }
+
+ if (ret < 0)
+ return -1;
+
+ if ((ret != len) || feof(dev->unrar_stream)) {
+ /* Nothing more to read. Return the whole blocks, if any,
+ * that we got. and adjust the file position back to the
+ * previous block boundary. */
+ size_t bytes = (size_t)blocks * DVD_VIDEO_LB_LEN - len; /* 'ret'? */
+ off_t over_read = -(bytes % DVD_VIDEO_LB_LEN);
+ /*off_t pos =*/ /*lseek(dev->fd, over_read, SEEK_CUR);*/
+ dev->current_pos += (off_t)len;
+ dev->seek_pos = dev->current_pos + (off_t)len + over_read;
+ /* minus over_read, I did not touch the code above, but I wonder
+ * if it is correct. It does not even use "ret" in the math. */
+
+ /* should have pos % 2048 == 0 */
+ return (int) (bytes / DVD_VIDEO_LB_LEN);
+ }
+
+ dev->current_pos += (off_t) ret;
+ dev->seek_pos += (off_t) ret;
+ len -= ret;
+ }
+
+ return blocks;
+}
+
+
+/**
+ * close the DVD device and clean up.
+ */
+static int rarfile_close(dvd_input_t dev)
+{
+
+ if (dev->unrar_stream)
+ pclose(dev->unrar_stream);
+ dev->unrar_stream = NULL;
+
+ if (dev->unrar_archive)
+ free(dev->unrar_archive);
+ dev->unrar_archive = NULL;
+
+ if (dev->unrar_file)
+ free(dev->unrar_file);
+ dev->unrar_file = NULL;
+
+ free(dev);
+
+ return 0;
+}
+
+
+
+int dvdinput_setupRAR(const char *unrar_path, const char *rarfile)
+{
+
+#ifdef DEBUG
+ fprintf(stderr, "libdvdread: Configuring for unrar.\n");
+#endif
+ /* Search for unrar in $PATH, common places etc, as well as "unrar_path"
+ *
+ * But we really need unrar with special SEEK -sk option for this to work
+ *
+ * If you want this to work on Windows, you have to use internal popen()
+ * functions, as Windows popen() does not work correctly.
+ * Please see: http://www.lundman.net/win32_popen.c
+ */
+
+ dvdinput_unrar_cmd = strdup(unrar_path);
+ dvdinput_unrar_file = strdup(rarfile);
+
+ /* Check it is present, and executable? */
+
+ /* libdvdcss replacement functions */
+ dvdinput_open = rarfile_open;
+ dvdinput_close = rarfile_close;
+ dvdinput_seek = rarfile_seek;
+ dvdinput_title = rarfile_title;
+ dvdinput_read = rarfile_read;
+ dvdinput_error = rarfile_error;
+
+ return 0;
+}
diff -ruib ../../versions/libdvdread_patch4/libdvdread/src/dvd_input.h
./dvd_input.h
--- ../../versions/libdvdread_patch4/libdvdread/src/dvd_input.h 2008-10-10
14:42:23.000000000 +0900
+++ ./dvd_input.h 2008-10-10 17:38:40.000000000 +0900
@@ -46,5 +46,6 @@
* Setup function accessed by dvd_reader.c. Returns 1 if there is CSS support.
*/
int dvdinput_setup(void);
+int dvdinput_setupRAR(const char *, const char *);
#endif /* LIBDVDREAD_DVD_INPUT_H */
diff -ruib ../../versions/libdvdread_patch4/libdvdread/src/dvd_reader.c
./dvd_reader.c
--- ../../versions/libdvdread_patch4/libdvdread/src/dvd_reader.c
2008-10-10 17:32:13.000000000 +0900
+++ ./dvd_reader.c 2008-10-10 17:37:45.000000000 +0900
@@ -273,6 +273,42 @@
return dvd;
}
+/*
+ * Open a image inside a rar. Takes extra filepath argument to the rar archive
+ */
+dvd_reader_t *DVDOpenRARImageFile( const char *unrar_cmd,
+ const char *rarfile,
+ const char *location )
+{
+ dvd_reader_t *dvd;
+ dvd_input_t dev;
+
+ dvdinput_setupRAR(unrar_cmd, rarfile);
+
+ dev = dvdinput_open( location );
+ if( !dev ) {
+ fprintf( stderr, "libdvdread: Can't openRAR %s:%s for reading\n",
+ rarfile, location);
+ return NULL;
+ }
+
+ dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+ if( !dvd ) {
+ dvdinput_close(dev);
+ return NULL;
+ }
+ dvd->isImageFile = 1;
+ dvd->dev = dev;
+ dvd->path_root = NULL;
+
+ dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+ dvd->udfcache = NULL;
+
+ dvd->css_title = 0;
+
+ return dvd;
+}
+
static dvd_reader_t *DVDOpenPath( const char *path_root )
{
dvd_reader_t *dvd;
diff -ruib ../../versions/libdvdread_patch4/libdvdread/src/dvd_reader.h
./dvd_reader.h
--- ../../versions/libdvdread_patch4/libdvdread/src/dvd_reader.h
2008-10-10 17:23:46.000000000 +0900
+++ ./dvd_reader.h 2008-10-10 17:38:17.000000000 +0900
@@ -104,6 +104,17 @@
*/
void DVDClose( dvd_reader_t * );
+
+/*
+ * Open an ImageFile contained in a RAR archive volume. This API call should
+ * probably be cleaned up.
+ * @lundman: 20080817
+ */
+dvd_reader_t *DVDOpenRARImageFile( const char *unrar_cmd,
+ const char *rarfile,
+ const char *location );
+
+
/**
*
*/
_______________________________________________
DVDnav-discuss mailing list
[email protected]
https://lists.mplayerhq.hu/mailman/listinfo/dvdnav-discuss