[MPlayer-users] Problematic DVD [SOLVED]

jezz at hkfree.org jezz at hkfree.org
Sat Sep 27 09:01:13 CEST 2008


Hi,
should I send this patch to mplayer devel mailing list in order to get it into
libdvdread?

On Pá 19.zář, jezz at hkfree.org wrote:
> Hi,
> according to UDF spec this DVD is broken. I tried to discover, why some players
> are ok (totem-xine on my system). Totem mounts the DVD and then plays files
> directly. From mplayer I can play VOB directly, but there is garbage in video
> (CSS?). DVDNav is not working. Kernel reads this DVD as ISO 9660 filesystem. So
> can libdvdread. I have implemented ISOFindFile(...) function from this spec:
> http://alumnus.caltech.edu/~pje/iso9660.html
> When file can't be located with UDF functions, then we can try ISO. This fix
> conforms with standards and plays my disks :)
> ISOFindFile is used as fallback - may be we can allow user to change the order
> with environment variable?
> Any comments are welcome.
> 
> On Pá  5.zář, Nico Sabbi wrote:
> > On Friday 05 September 2008 17:58:31 jezz at hkfree.org wrote:
> > > Hi,
> > > I have found the problem. It is in UDF structure. In ICB for
> > > /VIDEO_TS/VTS_37_0.VOB there is Tag Identifier 262 (Extended
> > > Attribute Header Descriptor) instead of common 261 (File Entry). I
> > > don't read UDF spec, so I don't know if this is correct, but with
> > > attached patch I can play this DVD.
> > >
> > > Jezz
> > >
> > > On So 30.srp, jezz at hkfree.org wrote:
> > > > Hi,
> > > > I have one DVD, that I can't play with mplayer. Some players are
> > > > fine. This dvd contains movie and boring tracks with
> > > > advertisments, etc. But I can't play track with movie (track 37).
> > 
> > interesting. Is there any publicly available spec regarding UDF?
> > Thanks for the patch, but before committing it I have to verify that
> > it's correct
> > _______________________________________________
> > MPlayer-users mailing list
> > MPlayer-users at mplayerhq.hu
> > https://lists.mplayerhq.hu/mailman/listinfo/mplayer-users
> 
> -- 
> Jezz
> mail:   jezz at hkfree.org
> jabber: jezz at njs.netlab.cz

> Index: src/dvd_reader.c
> ===================================================================
> --- src/dvd_reader.c	(revision 1148)
> +++ src/dvd_reader.c	(working copy)
> @@ -66,6 +66,7 @@
>  #endif
>  
>  #include "dvd_udf.h"
> +#include "dvd_iso9660.h"
>  #include "dvd_input.h"
>  #include "dvd_reader.h"
>  #include "md5.h"
> @@ -152,7 +153,16 @@
>  }
>  
>  
> +/* 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 )
>  {
> @@ -180,7 +190,7 @@
>  	} 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",
> @@ -197,7 +207,7 @@
>  
>  	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. */
> @@ -569,9 +579,9 @@
>      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;
>      }
>  
> @@ -707,7 +717,7 @@
>      } 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 ) );
> @@ -726,7 +736,7 @@
>  
>          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;
>          }
>      }
> Index: src/dvd_iso9660.c
> ===================================================================
> --- src/dvd_iso9660.c	(revision 0)
> +++ src/dvd_iso9660.c	(revision 0)
> @@ -0,0 +1,357 @@
> +/* vim:ts=4:sw=4:et
> + *
> + * Some parts taken from dvd_udf.c
> + *
> + * Miroslav Jezbera <jezz at hkfree.org>
> + *
> + * 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 "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);
> +    }
> Index: src/dvd_iso9660.h
> ===================================================================
> --- src/dvd_iso9660.h	(revision 0)
> +++ src/dvd_iso9660.h	(revision 0)
> @@ -0,0 +1,47 @@
> +/*
> + *
> + * Miroslav Jezbera <jezz at hkfree.org>
> + *
> + * 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 "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 */
> Index: src/Makefile.am
> ===================================================================
> --- src/Makefile.am	(revision 1148)
> +++ src/Makefile.am	(working copy)
> @@ -8,7 +8,8 @@
>  
>  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)
>  

-- 
Jezz
mail:   jezz at hkfree.org
jabber: jezz at njs.netlab.cz



More information about the MPlayer-users mailing list