[MPlayer-dev-eng] Read broken DVD's

Andreas Wendleder andreas.wendleder at googlemail.com
Wed Sep 22 01:29:58 CEST 2010


This enables mplayer to read broken DVD's incorrectly burned as ISO9660 
disks. This happens for example if one uses brasero's Data preset 
instead of the Video preset. Since W*****s Media Player can play those 
DVD's I thought we might be able to do so too.

 From 2d37d0a295654df8218688d64cd595d390cbc554 Mon Sep 17 00:00:00 2001
From: Andreas Wendleder <andreas.wendleder at gmail.com>
Date: Wed, 22 Sep 2010 01:20:43 +0200
Subject: [PATCH] Try to read broken DVD's.

This patch enables mplayer to read broken DVD's that were incorrectly
burned as ISO9660 images. Currently libdvdread refuses to read them
but the W*****s Media Player is able to play them.
With this patch no menues are shown but at least one is able to watch
DVD's for example burned with brasero and a "Data Disc" preset.

This patch was first proposed in
http://www.mail-archive.com/dvdnav-discuss@mplayerhq.hu/msg00674.html.
I just updated it to the latest mplayer svn.

aw

---
  Makefile          |    5 +-
  src/Makefile.am   |    3 +-
  src/dvd_iso9660.c |  357 
+++++++++++++++++++++++++++++++++++++++++++++++++++++
  src/dvd_iso9660.h |   47 +++++++
  src/dvd_reader.c  |   23 +++-
  5 files changed, 425 insertions(+), 10 deletions(-)
  create mode 100644 src/dvd_iso9660.c
  create mode 100644 src/dvd_iso9660.h

diff --git a/Makefile b/Makefile
index dcb9407..270e072 100644
--- a/Makefile
+++ b/Makefile
@@ -26,9 +26,10 @@ DVDREAD_HEADERS = src/dvdread/dvd_reader.h \
  	src/dvdread/nav_read.h \
  	src/dvdread/dvd_udf.h \
  	src/dvdread/nav_types.h \
-	src/dvdread/bitreader.h
+	src/dvdread/bitreader.h \
+	src/dvd_iso9660.h
  DVDREAD_SRCS = dvd_input.c dvd_reader.c dvd_udf.c ifo_print.c ifo_read.c \
-	md5.c nav_print.c nav_read.c bitreader.c
+	md5.c nav_print.c nav_read.c bitreader.c dvd_iso9660.c
  CFLAGS += -I$(SRC_PATH)/src

  LIB = $(L).a
diff --git a/src/Makefile.am b/src/Makefile.am
index 094e2b7..b928512 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,7 +8,8 @@ lib_LTLIBRARIES = libdvdread.la

  libdvdread_la_SOURCES = dvd_reader.c nav_read.c ifo_read.c \
  	dvd_input.c dvd_udf.c md5.c nav_print.c ifo_print.c bitreader.c \
-	bswap.h dvd_input.h dvdread_internal.h dvd_udf.h md5.h bitreader.h
+	bswap.h dvd_input.h dvdread_internal.h dvd_udf.h md5.h bitreader.h \
+	dvd_iso9660.c dvd_iso9660.h

  libdvdread_la_LIBADD = $(DYNAMIC_LD_LIBS)

diff --git a/src/dvd_iso9660.c b/src/dvd_iso9660.c
new file mode 100644
index 0000000..3d64cae
--- /dev/null
+++ b/src/dvd_iso9660.c
@@ -0,0 +1,357 @@
+/* vim:ts=4:sw=4:et
+ *
+ * Some parts taken from dvd_udf.c
+ *
+ * Miroslav Jezbera <[EMAIL PROTECTED]>
+ *
+ * dvd_iso9660: parse and read the ISO9660 filesystem (hybrid ISO+UDF 
DVD disks)
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "dvdread/dvd_reader.h"
+#include "dvd_iso9660.h"
+
+/* Private but located in/shared with dvd_reader.c */
+extern int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
+				size_t block_count, unsigned char *data,
+				int encrypted );
+
+/* Macros */
+#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) && 
!defined(WORDS_BIGENDIAN)
+#   define LITTLE_ENDIAN
+#endif /* LITTLE_ENDIAN */
+
+/*
+#define ISO_DEBUG 1
+*/
+
+#define FAILURE   0
+#define SUCCESS   1
+
+#define ISO_SECTOR_SIZE         DVD_VIDEO_LB_LEN
+#define ISO_TBL_REC_NAME_MAX    31
+#define ISO_MAX_PATH_NESTING    8
+
+#define ISO_FIRST_VOL_DESC_SECTOR   16
+#define ISO_LAST_VOL_DESC_SECTOR    17
+#define ISO_VOL_DESC_HEADER         "\01CD001\01"
+
+#define READ_WORD_LE(ptr)  (uint16_t)(((uint8_t *)ptr)[0] | ((uint8_t 
*)ptr)[1] << 8)
+#define READ_DWORD_LE(ptr) (uint32_t)(((uint8_t *)ptr)[0] | ((uint8_t 
*)ptr)[1] << 8 | \
+                            ((uint8_t *)ptr)[2] << 16 | ((uint8_t 
*)ptr)[3] << 24)
+
+#define IS_DIRECTORY(flags)   (((flags)&2)!=0)
+
+#ifdef LITTLE_ENDIAN
+#  define ISO_DIR_REC_FILE_POS   2
+#  define ISO_DIR_REC_FILE_SIZE 10
+#else /* !LITTLE_ENDIAN */
+#  define ISO_DIR_REC_FILE_POS   6
+#  define ISO_DIR_REC_FILE_SIZE 14
+#endif /* !LITTLE_ENDIAN */
+#define ISO_DIR_REC_FILE_FLAGS  25
+#define ISO_DIR_REC_NAME_LEN    32
+
+/* Types */
+typedef uint8_t iso_sector_t[ISO_SECTOR_SIZE];
+
+struct iso_volume_desc_t
+   {
+   char mSysId[33];
+   char mVolId[33];
+   uint32_t mSectorsCnt;
+   uint32_t mPathTblLen;
+   uint32_t mFirstPathTblStart;
+   uint32_t mSecondPathTblStart;
+   };
+
+struct iso_path_table_record_t
+   {
+   uint8_t  mRecordIdx;
+   uint8_t  mNameLen;
+   uint32_t mDirectoryFirstSector;
+   uint16_t mParentDirRecordIdx;
+   char     mName[ISO_TBL_REC_NAME_MAX+1];
+   struct iso_path_table_record_t *next;
+   };
+
+/* Common functions */
+char *Rtrim(char *str)
+   {
+   char *ptr;
+   if (str == NULL) return NULL;
+   ptr = str + strlen(str) - 1;
+   while (ptr >= str && isspace(ptr[0])) --ptr;
+   ptr[1] = '\0';
+   return str;
+   }
+
+/* Functions for ISO FS */
+int
+iso_read_volume_descriptor(dvd_reader_t *aDevice, struct 
iso_volume_desc_t *aVolumeDesc)
+    {
+    iso_sector_t content;
+    uint32_t sector;
+    int result = FAILURE;
+
+    for (sector = ISO_FIRST_VOL_DESC_SECTOR; sector <= 
ISO_LAST_VOL_DESC_SECTOR; ++sector)
+        {
+        if (UDFReadBlocksRaw(aDevice, sector, 1, content, 0) <= 0) 
continue;
+        if (strcmp((char *)content, ISO_VOL_DESC_HEADER) == 0)
+            {
+            result = SUCCESS;
+            break;
+            }
+        }
+    if (result != SUCCESS) return result;
+    memset((void *)aVolumeDesc, 0, sizeof(struct iso_volume_desc_t));
+    memcpy((void *)aVolumeDesc->mSysId, (void *)&content[8], 32);
+    memcpy((void *)aVolumeDesc->mVolId, (void *)&content[40], 32);
+    Rtrim((char *)aVolumeDesc->mSysId);
+    Rtrim((char *)aVolumeDesc->mVolId);
+#ifdef LITTLE_ENDIAN
+    aVolumeDesc->mSectorsCnt = *(uint32_t *)&content[80];
+    assert(*(uint16_t *)&content[128] == 2048);
+    aVolumeDesc->mPathTblLen = *(uint32_t *)&content[132];
+    aVolumeDesc->mFirstPathTblStart = *(uint32_t *)&content[140];
+    aVolumeDesc->mSecondPathTblStart = *(uint32_t *)&content[144];
+#else /* !LITTLE_ENDIAN */
+    aVolumeDesc->mSectorsCnt = *(uint32_t *)&content[84];
+    assert(*(uint16_t *)&content[130] == 2048);
+    aVolumeDesc->mPathTblLen = *(uint32_t *)&content[136];
+    aVolumeDesc->mFirstPathTblStart = *(uint32_t *)&content[148];
+    aVolumeDesc->mSecondPathTblStart = *(uint32_t *)&content[152];
+#endif /* !LITTLE_ENDIAN */
+    return SUCCESS;
+    }
+
+void
+iso_path_table_free(struct iso_path_table_record_t *list)
+    {
+    struct iso_path_table_record_t *ptr;
+    while (list != NULL)
+        {
+        ptr = list;
+        list = list->next;
+        free(ptr);
+        }
+    }
+
+int
+iso_read_path_table(dvd_reader_t *aDevice, struct iso_volume_desc_t 
*aVolumeDesc, struct iso_path_table_record_t **aRecords)
+    {
+    uint8_t *content, *ptr;
+    uint32_t sector, last_sector;
+    struct iso_path_table_record_t head, *current = &head;
+    uint8_t recordIdx = 0;
+
+    assert(aRecords != NULL);
+    if (aVolumeDesc->mPathTblLen == 0) return FAILURE;
+    sector = aVolumeDesc->mFirstPathTblStart;
+    last_sector = sector + (aVolumeDesc->mPathTblLen - 1) / 
ISO_SECTOR_SIZE;
+    content = (uint8_t *)malloc(ISO_SECTOR_SIZE * (last_sector - sector 
+ 1));
+    if (content == NULL) return FAILURE;
+    for (ptr = content; sector <= last_sector; ++sector, ptr += 
ISO_SECTOR_SIZE)
+        {
+        if (UDFReadBlocksRaw(aDevice, sector, 1, ptr, 0) <= 0)
+            {
+            free(content);
+            return FAILURE;
+            }
+        }
+    for (ptr = content; ptr < content + aVolumeDesc->mPathTblLen;)
+        {
+        current->next = (struct iso_path_table_record_t 
*)malloc(sizeof(struct iso_path_table_record_t));
+        if (current->next == NULL)
+            {
+            free(content);
+            iso_path_table_free(head.next);
+            return FAILURE;
+            }
+        current = current->next;
+        memset((void *)current, 0, sizeof(struct iso_path_table_record_t));
+        current->mRecordIdx = ++recordIdx;
+        current->mNameLen = *ptr;
+        assert(current->mNameLen > 0 && current->mNameLen <= 
ISO_TBL_REC_NAME_MAX);
+        current->mDirectoryFirstSector = READ_DWORD_LE(ptr + 2);
+        current->mParentDirRecordIdx = READ_WORD_LE(ptr + 6);
+        memcpy((void *)current->mName, ptr + 8, current->mNameLen);
+        ptr += 8 + ((current->mNameLen + 1) & ~1);
+#ifdef ISO_DEBUG
+        fprintf(stderr,
+                "Name length:             %u\n"
+                "Directory first sector:  %u\n"
+                "Parent directory record: %hu\n"
+                "Name:                    \"%s\"\n"
+                , (int)current->mNameLen, current->mDirectoryFirstSector,
+                current->mParentDirRecordIdx, current->mName);
+#endif /* ISO_DEBUG */
+        }
+    assert(content + aVolumeDesc->mPathTblLen == ptr);
+    free(content);
+    *aRecords = head.next;
+
+    return SUCCESS;
+    }
+
+uint32_t
+iso_search_directory(dvd_reader_t *aDevice, uint32_t aDirectorySector, 
char *aFilename, uint32_t *aFileSize)
+    {
+    uint32_t directorySize = ISO_SECTOR_SIZE, position, filePosition, 
fileSize;
+    uint8_t recordSize, identifierSize, flags;
+    iso_sector_t content;
+    char identifier[256];
+    int filenameLen;
+
+    *aFileSize = 0;
+    filenameLen = strlen(aFilename);
+    if (UDFReadBlocksRaw(aDevice, aDirectorySector, 1, content, 0) <= 0)
+        {
+        return 0;
+        }
+    for (position = 0; position < directorySize; position += recordSize)
+        {
+        if (position >= ISO_SECTOR_SIZE || content[position] == 0)
+            {
+            if (directorySize <= ISO_SECTOR_SIZE) return 0;
+            if (UDFReadBlocksRaw(aDevice, ++aDirectorySector, 1, 
content, 0) <= 0)
+                {
+                return 0;
+                }
+            position = 0;
+            directorySize -= ISO_SECTOR_SIZE;
+            }
+        recordSize = content[position];
+        if (recordSize == 0) break;
+        filePosition = READ_DWORD_LE(&content[position + 
ISO_DIR_REC_FILE_POS]);
+        fileSize = READ_DWORD_LE(&content[position + 
ISO_DIR_REC_FILE_SIZE]);
+        flags = content[position + ISO_DIR_REC_FILE_FLAGS];
+        identifierSize = content[position + ISO_DIR_REC_NAME_LEN];
+        memcpy((void *)identifier, (void *)&content[position + 
ISO_DIR_REC_NAME_LEN + 1], identifierSize);
+        identifier[identifierSize] = '\0';
+        if (identifierSize == 1 && identifier[0] == 0) /* current 
directory */
+            {
+            assert(IS_DIRECTORY(flags));
+            directorySize = fileSize;
+            }
+#ifdef ISO_DEBUG
+        fprintf(stderr,
+                "record size:   %u\n"
+                "file position: %u\n"
+                "file size:     %u\n"
+                "directory:     %d\n"
+                "name size:     %u\n"
+                "name:          \"%s\"\n"
+                , (int)recordSize, filePosition, fileSize, 
(int)IS_DIRECTORY(flags),
+                (int)identifierSize, identifier);
+#endif /* ISO_DEBUG */
+        if (!IS_DIRECTORY(flags) && (filenameLen == identifierSize ||
+                    (filenameLen < identifierSize && 
identifier[filenameLen] == ';')) &&
+                strncasecmp(aFilename, identifier, filenameLen) == 0)
+            {
+            return filePosition;
+            *aFileSize = fileSize;
+            }
+        }
+
+    return 0;
+    }
+
+/* Public interface */
+uint32_t ISOFindFile(dvd_reader_t *aDevice, char *aFilename, uint32_t 
*aFileSize)
+    {
+    struct iso_volume_desc_t volumeDesc;
+    struct iso_path_table_record_t *directories, *current;
+    char *path, *rest, *part, *file, last;
+    int parentIdx;
+    uint32_t directoryPosition;
+
+    *aFileSize = 0;
+    if (aFilename == NULL || aFilename[0] == '\0') return 0;
+    if (iso_read_volume_descriptor(aDevice, &volumeDesc) != SUCCESS)
+        {
+        return 0;
+        }
+#ifdef ISO_DEBUG
+    fprintf(stderr,
+            "System identifier:  \"%s\"\n"
+            "Volume identifier:  \"%s\"\n"
+            "Sectors count:      %u\n"
+            "Path table length:  %u\n"
+            "First path table:   %u\n"
+            "Second path table:  %u\n"
+            , volumeDesc.mSysId, volumeDesc.mVolId, 
volumeDesc.mSectorsCnt, volumeDesc.mPathTblLen,
+            volumeDesc.mFirstPathTblStart, volumeDesc.mSecondPathTblStart);
+#endif /* ISO_DEBUG */
+    if (iso_read_path_table(aDevice, &volumeDesc, &directories) != SUCCESS)
+        {
+        return 0;
+        }
+
+    parentIdx = 1; /* root directory */
+    current = directories;
+    path = rest = strdup(aFilename);
+    file = strrchr(path, '/');
+    if (file != NULL)
+        {
+        *file = '\0';
+        file = aFilename + (file - path + 1);
+        last = '/';
+        }
+    else
+        {
+        file = path;
+        last = '\0';
+        }
+    while (last != '\0' && *rest != '\0' && current != NULL)
+        {
+        while (*rest == '/') rest++;
+        part = rest;
+        while (*rest != '/' && *rest != '\0') rest++;
+        last = *rest;
+        *rest++ = '\0';
+        /* compare part of path with ISO path table entries */
+        while (current != NULL && (parentIdx != 
current->mParentDirRecordIdx ||
+                    strcasecmp(current->mName, part) != 0)) current = 
current->next;
+        if (current != NULL)
+            {
+            parentIdx = current->mRecordIdx;
+            }
+        }
+
+    if (current == NULL)
+        {
+        iso_path_table_free(directories);
+        free(path);
+        return 0;
+        }
+    directoryPosition = current->mDirectoryFirstSector;
+    iso_path_table_free(directories);
+    free(path);
+    return iso_search_directory(aDevice, directoryPosition, file, 
aFileSize);
+    }
diff --git a/src/dvd_iso9660.h b/src/dvd_iso9660.h
new file mode 100644
index 0000000..0ecc670
--- /dev/null
+++ b/src/dvd_iso9660.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Miroslav Jezbera <[EMAIL PROTECTED]>
+ *
+ * dvd_iso9660: parse and read the ISO9660 filesystem (hybrid ISO+UDF 
DVD disks)
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef DVD_ISO9660_H
+#define DVD_ISO9660_H 1
+
+#include <inttypes.h>
+
+#include "dvdread/dvd_reader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Looks for a file on the hybrid ISO9660/mini-UDF disc/imagefile and 
returns
+ * the block number where it begins, or 0 if it is not found.  The filename
+ * should be an absolute pathname on the UDF filesystem, starting with '/'.
+ * For example '/VIDEO_TS/VTS_01_1.IFO'.  On success, filesize will be 
set to
+ * the size of the file in bytes.
+ */
+uint32_t ISOFindFile(dvd_reader_t *aDevice, char *aFilename, uint32_t 
*aFileSize);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* DVD_ISO9660_H */
diff --git a/src/dvd_reader.c b/src/dvd_reader.c
index 3a94c1e..c8a280e 100644
--- a/src/dvd_reader.c
+++ b/src/dvd_reader.c
@@ -65,6 +65,8 @@ static inline int _private_gettimeofday( struct 
timeval *tv, void *tz )
  #endif

  #include "dvdread/dvd_udf.h"
+#include "dvd_iso9660.h"
+
  #include "dvd_input.h"
  #include "dvdread/dvd_reader.h"
  #include "md5.h"
@@ -150,7 +152,14 @@ void SetUDFCacheHandle(dvd_reader_t *device, void 
*cache)
    dev->udfcache = cache;
  }

-
+/* Find file on UDF first or try it on ISO9660 FS */
+uint32_t DVDFindFile( dvd_reader_t *device, char *filename, uint32_t 
*size )
+{
+    uint32_t result;
+    result = UDFFindFile(device, filename, size);
+    if (result > 0) return result;
+    return ISOFindFile(device, filename, size);
+}

  /* Loop over all titles and call dvdcss_title to crack the keys. */
  static int initAllCSSKeys( dvd_reader_t *dvd )
@@ -178,7 +187,7 @@ static int initAllCSSKeys( dvd_reader_t *dvd )
      } else {
        sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
      }
-    start = UDFFindFile( dvd, filename, &len );
+    start = DVDFindFile( dvd, filename, &len );
      if( start != 0 && len != 0 ) {
        /* Perform CSS key cracking for this title. */
        fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n",
@@ -195,7 +204,7 @@ static int initAllCSSKeys( dvd_reader_t *dvd )

      gettimeofday( &t_s, NULL );
      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
-    start = UDFFindFile( dvd, filename, &len );
+    start = DVDFindFile( dvd, filename, &len );
      if( start == 0 || len == 0 ) break;

      /* Perform CSS key cracking for this title. */
@@ -598,9 +607,9 @@ static dvd_file_t *DVDOpenFileUDF( dvd_reader_t 
*dvd, char *filename )
    uint32_t start, len;
    dvd_file_t *dvd_file;

-  start = UDFFindFile( dvd, filename, &len );
+  start = DVDFindFile( dvd, filename, &len );
    if( !start ) {
-    fprintf( stderr, "libdvdnav:DVDOpenFileUDF:UDFFindFile %s 
failed\n", filename );
+    fprintf( stderr, "libdvdnav:DVDOpenFileUDF:DVDFindFile %s 
failed\n", filename );
      return NULL;
    }

@@ -735,7 +744,7 @@ static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, 
int title, int menu )
    } else {
      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
    }
-  start = UDFFindFile( dvd, filename, &len );
+  start = DVDFindFile( dvd, filename, &len );
    if( start == 0 ) return NULL;

    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
@@ -754,7 +763,7 @@ static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, 
int title, int menu )

      for( cur = 2; cur < 10; cur++ ) {
        sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
-      if( !UDFFindFile( dvd, filename, &len ) ) break;
+      if( !DVDFindFile( dvd, filename, &len ) ) break;
        dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
      }
    }
-- 
1.7.0.4


More information about the MPlayer-dev-eng mailing list