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

Reply via email to