[libdvdnav-devel] [Git][videolan/libdvdread][master] 11 commits: DVD-Audio: Add Audio Still Video Set (ASVS) IFO structures

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sun Dec 7 20:40:01 UTC 2025



Jean-Baptiste Kempf pushed to branch master at VideoLAN / libdvdread


Commits:
44182853 by Saifelden Mohamed Ismail at 2025-12-05T12:58:18+02:00
DVD-Audio: Add Audio Still Video Set (ASVS) IFO structures

- - - - -
6e98b103 by Saifelden Mohamed Ismail at 2025-12-05T14:20:01+02:00
DVD-Audio: add still frame structures in ATS

- - - - -
32beeadf by Saifelden Mohamed Ismail at 2025-12-05T14:20:13+02:00
DVD-Audio: Add Audio Still Video Set (ASVS) IFO reads

- - - - -
148b4df1 by Saifelden Mohamed Ismail at 2025-12-07T16:12:37+02:00
DVD-Audio: Add asvs enum members

- - - - -
b1252962 by Saifelden Mohamed Ismail at 2025-12-07T16:12:41+02:00
DVD-Audio: Add asvs ifo and backup file open and stat

- - - - -
01f411d5 by Saifelden Mohamed Ismail at 2025-12-07T16:12:41+02:00
DVD-Audio: expose audio_sv.vob menu in DVDOpenFile

- - - - -
5c3bbafd by Saifelden Mohamed Ismail at 2025-12-07T16:12:41+02:00
DVD-Audio: expose Audio Still Video Set (ASVS) ifo and backup

- - - - -
d272808f by Saifelden Mohamed Ismail at 2025-12-07T16:12:41+02:00
DVD-Audio: Add function prototype for set_stream

- - - - -
39d78008 by Saifelden Mohamed Ismail at 2025-12-07T16:12:41+02:00
DVD-Audio: introduce stream type member and setter function

- - - - -
75e2685d by Saifelden Mohamed Ismail at 2025-12-07T16:12:41+02:00
DVD-Audio: Make css and cpxm decryption available simultaneously

- - - - -
482f0ab6 by Saifelden Mohamed Ismail at 2025-12-07T16:12:41+02:00
DVD-Audio: fix stream (AOB, VOB) type logic

- - - - -


6 changed files:

- src/dvd_input.c
- src/dvd_input.h
- src/dvd_reader.c
- src/dvdread/dvd_reader.h
- src/dvdread/ifo_types.h
- src/ifo_read.c


Changes:

=====================================
src/dvd_input.c
=====================================
@@ -75,8 +75,6 @@ int         (*dvdinput_init)  (dvd_input_t, uint8_t* mkb);
 # define DVDcpxm_open_stream(a, b) \
     dvdcpxm_open_stream((void*)(a), (dvdcss_stream_cb*)(b))
 # define DVDcpxm_open(a) dvdcss_open((char*)(a))
-# define DVDcpxm_close   dvdcpxm_close
-# define DVDcpxm_seek    dvdcpxm_seek
 # define DVDcpxm_read    dvdcpxm_read
 # define DVDcpxm_init    dvdcpxm_init
 #endif
@@ -102,10 +100,6 @@ static int      (*DVDcss_read)  (dvdcss_t, void *, int, int);
 #define DVDCSS_SEEK_KEY (1 << 1)
 /* function to setup the cpxm struct */
 #ifdef HAVE_DVDCSS_DVDCPXM_H
-static dvdcss_t (*DVDcpxm_open_stream) (void *, dvdcss_stream_cb *);
-static dvdcss_t (*DVDcpxm_open)  (const char *);
-static int      (*DVDcpxm_close) (dvdcss_t);
-static int      (*DVDcpxm_seek)  (dvdcss_t, int, int);
 static int      (*DVDcpxm_read)  (dvdcss_t, void *, int, int);
 static int      (*DVDcpxm_init)  (dvdcss_t, uint8_t* p_mkb);
 #endif
@@ -139,6 +133,12 @@ struct dvd_input_s {
   dvd_logger_cb *logcb;
   off_t ipos;
 
+  /* This variable keeps track of the current files stream_type,
+   * and in turn determined the decryption method to use */
+  /* DVD_A -> AOB */
+  /* DVD_V -> VOB */
+  dvd_type_t stream_type;
+
   /* dummy file input */
   int fd;
   /* stream input */
@@ -153,6 +153,7 @@ static dvd_input_t dvd_input_New(void *priv, dvd_logger_cb *logcb)
       dev->priv = priv;
       dev->logcb = logcb;
       dev->ipos = 0;
+      dev->stream_type = DVD_V;
 
       /* Initialize all inputs to safe defaults */
       dev->dvdcss = NULL;
@@ -215,6 +216,11 @@ static int css_seek(dvd_input_t dev, int blocks)
  */
 static int css_title(dvd_input_t dev, int block)
 {
+#ifdef HAVE_DVDCSS_DVDCPXM_H
+  if (dev->stream_type == DVD_A)
+    return DVDcss_seek(dev->dvdcss, block, DVDINPUT_NOFLAGS);
+  else
+#endif
   return DVDcss_seek(dev->dvdcss, block, DVDCSS_SEEK_KEY);
 }
 
@@ -223,6 +229,11 @@ static int css_title(dvd_input_t dev, int block)
  */
 static int css_read(dvd_input_t dev, void *buffer, int blocks, int flags)
 {
+#ifdef HAVE_DVDCSS_DVDCPXM_H
+  if (dev->stream_type == DVD_A)
+    return DVDcpxm_read(dev->dvdcss, buffer, blocks, flags);
+  else
+#endif
   return DVDcss_read(dev->dvdcss, buffer, blocks, flags);
 }
 
@@ -241,34 +252,6 @@ static int css_close(dvd_input_t dev)
 }
 
 #ifdef HAVE_DVDCSS_DVDCPXM_H
-/**
- * seek into the device.
- */
-static int cpxm_seek(dvd_input_t dev, int blocks)
-{
-  /* DVDINPUT_NOFLAGS should match the DVDCSS_NOFLAGS value. */
-  return DVDcpxm_seek(dev->dvdcss, blocks, DVDINPUT_NOFLAGS);
-}
-
-/**
- * read data from the device.
- */
-static int cpxm_read(dvd_input_t dev, void *buffer, int blocks, int flags)
-{
-  return DVDcpxm_read(dev->dvdcss, buffer, blocks, flags);
-}
-
-static int cpxm_close(dvd_input_t dev)
-{
-  int ret;
-
-  ret = DVDcpxm_close(dev->dvdcss);
-
-  free(dev);
-
-  return ret;
-}
-
 /**
  * Setup Datastructure.
  */
@@ -526,63 +509,49 @@ int dvdinput_setup(void *priv, dvd_logger_cb *logcb, dvd_type_t dvda_flag)
   /* Locate the functions, DVD_V or DVD_A */
   if(dvdcss_library != NULL) {
     /* functions should have the same template*/
-    switch(dvda_flag) {
-      case DVD_V:
-      /* hybrid discs encrypt video tracks with css*/
-        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 "
-                    "https://www.videolan.org/" );
-        } else if( ( !DVDcss_open || !DVDcss_close || !DVDcss_seek
-            || !DVDcss_read ) &&  dvda_flag == DVD_V ) {
-          DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
-                     "Missing symbols in %s, "
-                    "this shouldn't happen !", CSS_LIB);
-          dlclose(dvdcss_library);
-          dvdcss_library = NULL;
-        }
-      break;
-      case DVD_A:
+    /* hybrid discs encrypt video tracks with css*/
+    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 (dvda_flag == DVD_A) {
 #ifdef HAVE_DVDCSS_DVDCPXM_H
-        DVDcpxm_open_stream = (dvdcss_t (*)(void *, dvdcss_stream_cb *))
-          dlsym(dvdcss_library, U_S "dvdcss_open_stream");
-        DVDcpxm_open = (dvdcss_t (*)(const char*))
-          dlsym(dvdcss_library, U_S "dvdcss_open");
-        DVDcpxm_close = (int (*)(dvdcss_t))
-          dlsym(dvdcss_library, U_S "dvdcpxm_close");
-        DVDcpxm_seek = (int (*)(dvdcss_t, int, int))
-          dlsym(dvdcss_library, U_S "dvdcpxm_seek");
-        DVDcpxm_read = (int (*)(dvdcss_t, void*, int, int))
-          dlsym(dvdcss_library, U_S "dvdcpxm_read");
-        DVDcpxm_init = (int (*)(dvdcss_t, uint8_t *p_mkb))
-          dlsym(dvdcss_library, U_S "dvdcpxm_init");
-
-        if( ( !DVDcpxm_open || !DVDcpxm_close || !DVDcpxm_seek
-                  || !DVDcpxm_read || !DVDcpxm_init ) && dvda_flag == DVD_A ) {
-          DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
-                     "Missing symbols in %s, "
-                    "DVD-Audio support not present in libdvdcss", CSS_LIB);
-          dlclose(dvdcss_library);
-          dvdcss_library = NULL;
-        }
-#else /* We are trying to open a DVD-Audio, but we don't have the DVDcss CPXM */
-        DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
-                "DVD-Audio headers not present, update the DVDCSS library");
-        dlclose(dvdcss_library);
-        dvdcss_library = NULL;
+      DVDcpxm_read = (int (*)(dvdcss_t, void*, int, int))
+        dlsym(dvdcss_library, U_S "dvdcpxm_read");
+      DVDcpxm_init = (int (*)(dvdcss_t, uint8_t *p_mkb))
+        dlsym(dvdcss_library, U_S "dvdcpxm_init");
+#else
+    DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+            "DVD-Audio headers not present, update the DVDCSS library");
+    dlclose(dvdcss_library);
+    dvdcss_library = NULL;
+#endif
+    }
+    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 "
+                "https://www.videolan.org/" );
+    } 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;
+#ifdef HAVE_DVDCSS_DVDCPXM_H
+      } else if(!DVDcpxm_read || !DVDcpxm_init) {
+      DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+              "Missing symbols for DVD-Audio in %s, "
+              "this shouldn't happen !", CSS_LIB);
+      dlclose(dvdcss_library);
+      dvdcss_library = NULL;
 #endif
-      break;
     }
   }
 #endif /* HAVE_DVDCSS_DVDCSS_H */
@@ -596,26 +565,19 @@ int dvdinput_setup(void *priv, dvd_logger_cb *logcb, dvd_type_t dvda_flag)
     */
 
     /* libdvdcss wrapper functions */
-    switch(dvda_flag){
-      case DVD_V:
-        dvdinput_open  = css_open;
-        dvdinput_close = css_close;
-        dvdinput_seek  = css_seek;
-        dvdinput_title = css_title;
-        dvdinput_read  = css_read;
-        break;
-      case DVD_A:
+    dvdinput_open  = css_open;
+    dvdinput_close = css_close;
+    dvdinput_seek  = css_seek;
+    dvdinput_title = css_title;
+    dvdinput_read  = css_read;
+
+    /* additional setup function that must be run for DVD_A decryption */
+    if (dvda_flag == DVD_A) {
 #ifdef HAVE_DVDCSS_DVDCPXM_H
-        dvdinput_open  = css_open;
-        dvdinput_close = cpxm_close;
-        dvdinput_seek  = cpxm_seek;
-        dvdinput_title = cpxm_seek; /* cpxm title is just seek */
-        dvdinput_read  = cpxm_read;
-        dvdinput_init  = cpxm_init;
+    dvdinput_init  = cpxm_init;
 #else
-        assert(!"libdvdcss compiled without DVD-Audio (CPXM) support");
+    assert(!"libdvdcss compiled without DVD-Audio (CPXM) support");
 #endif
-        break;
     }
     return 1;
 
@@ -632,3 +594,9 @@ int dvdinput_setup(void *priv, dvd_logger_cb *logcb, dvd_type_t dvda_flag)
     return 0;
   }
 }
+
+/* change the stream type, this will set the decryption method */
+void dvdinput_set_stream(dvd_input_t dev, dvd_type_t decryption_type)
+{
+    dev->stream_type = decryption_type;
+}


=====================================
src/dvd_input.h
=====================================
@@ -65,6 +65,8 @@ extern int         (*dvdinput_title) (dvd_input_t, int);
 extern int         (*dvdinput_read)  (dvd_input_t, void *, int, int);
 /* run to initialize DVD-Audio encryption */
 extern int         (*dvdinput_init)  (dvd_input_t, uint8_t* mkb);
+
+extern void        dvdinput_set_stream(dvd_input_t, dvd_type_t);
 /**
  * Setup function accessed by dvd_reader.c.  Returns 1 if there is CSS support.
  */


=====================================
src/dvd_reader.c
=====================================
@@ -1011,12 +1011,26 @@ static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *ctx, int title, int menu )
   char filename[ MAX_UDF_FILE_NAME_LEN ];
   uint32_t start, len;
   dvd_file_t *dvd_file;
-
+  /* stream type must be set to determine decryption method*/
+  /* DVD-Audio discs contain both AOBs and VOBs */
+  /* DVD_V = VOB, DVD_A = AOB */
+  dvd_type_t stream_type;
   if( title == 0 ) {
+    stream_type = DVD_V;
     sprintf( filename, "/%s_TS/%s_TS.VOB", DVD_TYPE_STRING( ctx->dvd_type ), DVD_TYPE_STRING( ctx->dvd_type ) );
+  } else if(!menu) {
+    /* DVD Content - Tracks/Chapters  */
+    stream_type = ctx->dvd_type;
+    sprintf( filename, "/%s_TS/%cTS_%02d_1.%cOB", DVD_TYPE_STRING( ctx->dvd_type ),
+            STREAM_TYPE_STRING( ctx->dvd_type ), title, STREAM_TYPE_STRING( ctx->dvd_type ) );
   } else {
-    sprintf( filename, "/%s_TS/%cTS_%02d_%d.%cOB", DVD_TYPE_STRING( ctx->dvd_type ), STREAM_TYPE_STRING( ctx->dvd_type ),
-            title, menu ? 0 : 1, STREAM_TYPE_STRING( ctx->dvd_type ) );
+    stream_type = DVD_V;
+    if ( ctx->dvd_type == DVD_V )
+      /* DVD_Video title menu */
+      sprintf( filename, "/VIDEO_TS/VTS_%02d_0.VOB", title );
+    else if ( ctx->dvd_type == DVD_A )
+      /* DVD_Audio title menu */
+      sprintf( filename, "/AUDIO_TS/AUDIO_SV.VOB" );
   }
   start = UDFFindFile( ctx, filename, &len );
   if( start == 0 ) return NULL;
@@ -1026,7 +1040,7 @@ static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *ctx, int title, int menu )
   dvd_file->ctx = ctx;
 
   /* css vars not used in CPXM */
-  if( ctx->dvd_type == DVD_V )
+  if( stream_type == DVD_V )
       /*Hack*/ dvd_file->css_title = title << 1 | menu;
 
   dvd_file->lb_start = start;
@@ -1044,7 +1058,7 @@ static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *ctx, int title, int menu )
     }
   }
 
-  if( ctx->dvd_type == DVD_V && ctx->rd->css_state == 1 /* Need key init */ ) {
+  if( stream_type == DVD_V && ctx->rd->css_state == 1 /* Need key init */ ) {
     initAllCSSKeys( ctx );
     ctx->rd->css_state = 2;
   }
@@ -1054,6 +1068,7 @@ static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *ctx, int title, int menu )
   }
   */
 
+  dvdinput_set_stream( ctx->rd->dev, stream_type );
   return dvd_file;
 }
 
@@ -1076,11 +1091,15 @@ static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu )
     dvd_input_t dev;
 
     if( title == 0 ) {
-      /* there can not be an AUDIO_TS.AOB, there is however sometimes an AUDIO_TS.VOB menu */
+      /* the root menu is AUDIO_TS.VOB or VIDEO_TS.VOB */
       sprintf(filename, "%s_TS.VOB", DVD_TYPE_STRING( ctx->dvd_type ) );
-    } else if ( ctx->dvd_type == DVD_V ) {
+    } else {
       /* there are no ATS_%02i_0.AOB's */
-      sprintf( filename, "VTS_%02i_0.VOB", title );
+      if ( ctx->dvd_type == DVD_V ) 
+        sprintf( filename, "VTS_%02i_0.VOB", title );
+      else
+        /* Remaining title menus would be in the still videos VOB */
+        sprintf( filename, "AUDIO_SV.VOB" );
     }
     if( !findDVDFile( ctx, filename, full_path ) ) {
       free( dvd_file );
@@ -1103,6 +1122,8 @@ static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu )
     dvd_file->title_devs[ 0 ] = dev;
     dvdinput_title( dvd_file->title_devs[0], 0);
     dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+    /* menus are always VOB streams */
+    dvdinput_set_stream(dev, DVD_V);
 
   } else {
     int i;
@@ -1121,6 +1142,8 @@ static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu )
 
       dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
       dvd_file->title_devs[ i ] = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
+      /* setting type of stream will determine what decryption to use */
+      dvdinput_set_stream( dvd_file->title_devs[ i ], ctx->dvd_type );
       dvdinput_title( dvd_file->title_devs[ i ], 0 );
       dvd_file->filesize += dvd_file->title_sizes[ i ];
     }
@@ -1167,12 +1190,14 @@ dvd_file_t *DVDOpenFile( dvd_reader_t *ctx, int titlenum,
     break;
   case DVD_READ_MENU_VOBS:
     if( dvd->isImageFile ) {
-      /* there is only one DVD-Audio menu vob, this call should be restricted */
-      if ( ctx->dvd_type == DVD_A && titlenum != 0 ) 
+      /* there is only two DVD-Audio menu vobs, and the second is optional
+       * in the case of DVD-Audio, this should return 0 for AUDIO_TS.VOB, which is the main menu
+       * AUDIO_SV.VOB is the Audio Still Video Set (ASVS), and contains the title menus */
+      if ( ctx->dvd_type == DVD_A && titlenum > 1 )
         Log2( ctx, "Defaulting to the only menu on DVD-Audio discs" );
-      return DVDOpenVOBUDF( ctx, ( ctx->dvd_type == DVD_V ? titlenum : 0 ), 1 );
+      return DVDOpenVOBUDF( ctx, ( ctx->dvd_type == DVD_V ? titlenum : titlenum > 0 ), 1 );
     } else {
-      return DVDOpenVOBPath( ctx, ( ctx->dvd_type == DVD_V ? titlenum : 0 ), 1 );
+      return DVDOpenVOBPath( ctx, ( ctx->dvd_type == DVD_V ? titlenum : titlenum > 0 ), 1 );
     }
     break;
   case DVD_READ_TITLE_VOBS:
@@ -1190,6 +1215,24 @@ dvd_file_t *DVDOpenFile( dvd_reader_t *ctx, int titlenum,
       return NULL;
     }
     strcpy( filename, "/AUDIO_TS/AUDIO_PP.IFO" );
+    break;
+  case DVD_READ_ASVS_INFO:
+    /* no other way to reach ASVS menu*/
+    if( ctx->dvd_type == DVD_V ) {
+      Log1( ctx, "ASVS IFO is exclusive to DVD-Audio" );
+      return NULL;
+    }
+    strcpy( filename, "/AUDIO_TS/AUDIO_SV.IFO" );
+
+    break;
+  case DVD_READ_ASVS_INFO_BACKUP:
+    /* no other way to reach ASVS menu*/
+    if( ctx->dvd_type == DVD_V ) {
+      Log1( ctx, "ASVS IFO Backup is exclusive to DVD-Audio" );
+      return NULL;
+    }
+    strcpy( filename, "/AUDIO_TS/AUDIO_SV.BUP" );
+
     break;
   default:
     Log1( ctx, "Invalid domain for file open." );
@@ -1383,6 +1426,24 @@ int DVDFileStat( dvd_reader_t *reader, int titlenum,
     }
     strcpy( filename, "/AUDIO_TS/AUDIO_PP.IFO" );
 
+    break;
+  case DVD_READ_ASVS_INFO:
+    /* no other way to reach ASVS menu*/
+    if( reader->dvd_type == DVD_V ) {
+      Log1( reader, "ASVS IFO is exclusive to DVD-Audio" );
+      return -1;
+    }
+    strcpy( filename, "/AUDIO_TS/AUDIO_SV.IFO" );
+
+    break;
+  case DVD_READ_ASVS_INFO_BACKUP:
+    /* no other way to reach ASVS menu*/
+    if( reader->dvd_type == DVD_V ) {
+      Log1( reader, "ASVS IFO Backup is exclusive to DVD-Audio" );
+      return -1;
+    }
+    strcpy( filename, "/AUDIO_TS/AUDIO_SV.BUP" );
+
     break;
   default:
     Log1(reader, "Invalid domain for file stat." );


=====================================
src/dvdread/dvd_reader.h
=====================================
@@ -187,6 +187,8 @@ typedef enum {
                                   the title set are opened and read as a
                                   single file. */
   DVD_READ_SAMG_INFO,        /* for the AUDIO_PP.IFO */
+  DVD_READ_ASVS_INFO,        /* for the AUDIO_SV.IFO */
+  DVD_READ_ASVS_INFO_BACKUP, /* for the AUDIO_SV.BUP */
 } dvd_read_domain_t;
 
 /**


=====================================
src/dvdread/ifo_types.h
=====================================
@@ -913,6 +913,27 @@ typedef struct {
 } ATTRIBUTE_PACKED atsi_track_pointer_t;
 #define ATSI_TRACK_POINTER_SIZE 12U
 
+/* the bellow entries are likely related to still frames, they are still largly undocumented
+ * likely contain some information on duration of still images */
+typedef struct {
+  uint8_t  unknown_1; /* always 0x01 */
+  uint8_t  unknown_2; /* probably a flag. sometimes 00, sometimes 04 */
+  uint16_t start_value; /* seems like a set of ranges, next entry starts where this ends */
+  uint16_t end_value;
+} ATTRIBUTE_PACKED atsi_asvs_range_t;
+#define ATSI_ASVS_RANGES_TABLE_SIZE 6U
+
+/* there is one set of these per frame group, resets at 01 when at start of group */
+typedef struct {
+  uint8_t  frame_index; /* overall frame index? */
+  uint8_t  unknown_1; /* zero? */
+  uint16_t unknown_2;
+  uint16_t unknown_3;
+  uint16_t unknown_4;
+  uint16_t unknown_5;
+} ATTRIBUTE_PACKED atsi_asvs_frame_t;
+#define ATSI_ASVS_FRAME_TABLE_SIZE 10U
+
 typedef struct {
   uint16_t unknown_1; /* will be 0x0000*/
   uint8_t  nr_tracks; /* unsure if this holds up for other files*/
@@ -925,6 +946,10 @@ typedef struct {
   uint16_t unknown_5; /* will be 0x0000*/
   atsi_track_timestamp_t *atsi_track_timestamp_rows; /*length determined by nr_tracks*/
   atsi_track_pointer_t   *atsi_track_pointer_rows;
+
+  /* these entries only exist when theres an ASVS */
+  atsi_asvs_range_t      *atsi_asvs_range_rows; /* length determined by nr_tracks */
+  atsi_asvs_frame_t      *atsi_track_still_frame_rows;  /* length determined by the ASVS struct, total nr of frames */
 } ATTRIBUTE_PACKED atsi_title_record_t;
 #define ATSI_TITLE_ROW_TABLE_SIZE 16U
 
@@ -937,6 +962,75 @@ typedef struct {
 } ATTRIBUTE_PACKED atsi_title_table_t;
 #define ATSI_TITLE_TABLE_SIZE 8U
 
+/**
+ * ASVS
+ *
+ * Structures relating to the Audio Still Video Set (ASVS).
+ */
+
+/**
+ * ASVS Frame Groupings (unofficial term):
+ *
+ * Frame groupings are unrelated to DVD-Audio audio groups. When multiple 
+ * groupings exist, they represent sets of frames per audio track (typically
+ * lyric pages or per-track menus). Each DVD-Audio trackpoint requires an
+ * associated still frame.
+ *
+ * Multiple groupings present:
+ *   - Frame records use relative sector offsets from grouping's start_sector
+ *   - High bit (0x8000) set in frame_record_t marks grouping boundaries
+ *
+ * Single grouping only:
+ *   - Frame records contain absolute sector addresses (high bit clear)
+ *   - Seems to indicates simple still frames without menus/lyrics
+ *
+ * The unknown2[72] block likely contains encoder metadata (resolution, color)
+ * and is likely not required for frame extraction.
+ *
+ * there seems to be header offset in the vob file of 35 bytes
+ */
+
+ /**
+ * ASVS Group Entry
+ * Describes a contiguous sequence of frames in the MPEG stream,
+ */
+typedef struct {
+  uint8_t  nr_frames;
+  uint8_t  unknown;
+  uint16_t start_frame; /* seems to be the number of the start frame (index + 1) */
+  uint16_t zero1;
+  uint16_t start_sector; /* start sector of frame VOBU packet in AUDIO_TS.VOB */
+} ATTRIBUTE_PACKED asvs_group_t;
+#define ASVS_GROUP_SIZE 8U
+
+#define ASVS_GROUP_MAX_SIZE 99U
+
+/**
+ * offset in sectors relative to the group start_sector
+ * total number of frames is the sum of nr_frames for each element in asvs_groups 
+ * If high bit set (0x8000): relative offset within group
+ * Otherwise: absolute frame position
+ */
+typedef uint16_t frame_record_t;
+#define ASVS_FRAME_RECORD_SIZE 2U
+
+/**
+ * Audio Still Video Set Management Table. Exclusive to DVD-Audio discs
+ */
+typedef struct {
+  char          asvs_identifier[12]; /* DVDAUDIOASVS */
+  uint16_t      asvs_nr_groups; /* number of elements in asvs_groups */
+  uint16_t      specification_version; /* always 0x0012 */
+  uint16_t      zero1;
+  uint16_t      unknown1; /* always 0002 */
+  uint16_t      zero2;
+  uint16_t      length_sectors; /* seems like length of AUDIO_TS.VOB in sectors */
+  uint8_t       unknown2[72]; /* not sure what these are. 0x00108080 repeateds alot */
+  asvs_group_t  asvs_groups[ASVS_GROUP_MAX_SIZE]; /* size determined by asvs_nr_groups */
+  frame_record_t *frame_offsets_sectors;
+} ATTRIBUTE_PACKED asvs_mat_t;
+#define ASVS_MAT_SIZE 888U
+
 /**
  * PartOfTitle Unit Information.
  */
@@ -1058,6 +1152,9 @@ typedef struct {
       /* ATSI */
       atsi_mat_t             *atsi_mat;
       atsi_title_table_t     *atsi_title_table;
+
+      /* ASVS */
+      asvs_mat_t             *asvs_mat;
     };
 
   };


=====================================
src/ifo_read.c
=====================================
@@ -94,6 +94,8 @@ static int ifoRead_VMG(ifo_handle_t *ifofile);
 static int ifoRead_AMG(ifo_handle_t *ifofile);
 /* Can be used to make simple dvd-a playback, no menus*/
 static int ifoRead_SAMG(ifo_handle_t *ifofile);
+/* for the still video set */
+static int ifoRead_ASVS(ifo_handle_t *ifofile);
 
 static int ifoRead_VTS(ifo_handle_t *ifofile);
 static int ifoRead_ATS(ifo_handle_t *ifofile);
@@ -192,7 +194,7 @@ CHECK_STRUCT_SIZE(atsi_mat_t,             0, ATSI_MAT_SIZE);
 CHECK_STRUCT_SIZE(atsi_title_index_t,     0, ATSI_TITLE_INDEX_SIZE);
 CHECK_STRUCT_SIZE(atsi_track_timestamp_t, 0, ATSI_TRACK_TIMESTAMP_SIZE);
 CHECK_STRUCT_SIZE(atsi_track_pointer_t,   0, ATSI_TRACK_POINTER_SIZE);
-CHECK_STRUCT_SIZE(atsi_title_record_t,    2, ATSI_TITLE_ROW_TABLE_SIZE);
+CHECK_STRUCT_SIZE(atsi_title_record_t,    4, ATSI_TITLE_ROW_TABLE_SIZE);
 CHECK_STRUCT_SIZE(atsi_title_table_t,     2, ATSI_TITLE_TABLE_SIZE);
 CHECK_STRUCT_SIZE(downmix_coeff_t,        0, DOWNMIX_COEFF_SIZE);
 
@@ -471,6 +473,16 @@ static ifo_handle_t *ifoOpenFileOrBackup(dvd_reader_t *ctx, int title,
     if(!ifoRead_SAMG(ifofile))
       goto ifoOpen_fail;
 
+    /* The Audio Still Video Set (ASVS) is optional, though usually exists */
+    ifop->file = DVDOpenFile(ctx, 2, backup ? DVD_READ_ASVS_INFO_BACKUP
+                             : DVD_READ_ASVS_INFO);
+    if(!ifop->file)
+      Log1(ctx, "Disc does not have a still video set");
+    else {
+      if(!ifoRead_ASVS(ifofile))
+        goto ifoOpen_fail;
+    }
+
     return ifofile;
   }
 
@@ -825,6 +837,75 @@ static int ifoRead_SAMG(ifo_handle_t *ifofile) {
   return 1;
 }
 
+static int ifoRead_ASVS(ifo_handle_t *ifofile){
+
+  struct ifo_handle_private_s *ifop = PRIV(ifofile);
+  asvs_mat_t *asvs_mat;
+
+  asvs_mat = calloc(1, sizeof(asvs_mat_t));
+
+  if(!asvs_mat)
+    return 0;
+
+  ifofile->asvs_mat = asvs_mat;
+
+  if(!DVDFileSeek_(ifop->file, 0)) {
+    free(ifofile->asvs_mat);
+    ifofile->asvs_mat = NULL;
+    return 0;
+  }
+
+  if(!DVDReadBytes(ifop->file, asvs_mat, ASVS_MAT_SIZE)) {
+    free(ifofile->asvs_mat);
+    ifofile->asvs_mat = NULL;
+    return 0;
+  }
+
+  if(strncmp("DVDAUDIOASVS", asvs_mat->asvs_identifier, 12) != 0) {
+    free(ifofile->asvs_mat);
+    ifofile->asvs_mat = NULL;
+    return 0;
+  }
+
+  B2N_16(asvs_mat->asvs_nr_groups);
+  B2N_16(asvs_mat->specification_version);
+  B2N_16(asvs_mat->length_sectors);
+
+  int total_nr_frames = 0;
+  for (int i = 0; i < asvs_mat->asvs_nr_groups; i++ ) {
+    B2N_16(asvs_mat->asvs_groups[i].start_frame);
+    B2N_16(asvs_mat->asvs_groups[i].start_sector);
+    CHECK_ZERO(asvs_mat->asvs_groups[i].zero1);
+    total_nr_frames+=asvs_mat->asvs_groups[i].start_sector;
+  }
+
+
+  asvs_mat->frame_offsets_sectors = calloc(total_nr_frames, ASVS_FRAME_RECORD_SIZE);
+  if(!asvs_mat->frame_offsets_sectors) {
+    free(ifofile->asvs_mat);
+    ifofile->asvs_mat = NULL;
+    return 0;
+  }
+
+  if(!DVDReadBytes(ifop->file, asvs_mat->frame_offsets_sectors,
+                   total_nr_frames * ASVS_FRAME_RECORD_SIZE )) {
+    free(asvs_mat->frame_offsets_sectors);
+    free(ifofile->asvs_mat);
+    ifofile->asvs_mat = NULL;
+    return 0;
+  }
+
+  for(int i = 0; i < total_nr_frames; i++)
+    B2N_16(asvs_mat->frame_offsets_sectors[i]);
+
+
+  CHECK_VALUE(asvs_mat->specification_version == 0x0012);
+  CHECK_ZERO(asvs_mat->zero1);
+  CHECK_ZERO(asvs_mat->zero2);
+  return 1;
+
+}
+
 int ifoRead_TT(ifo_handle_t *ifofile) {
 
   struct ifo_handle_private_s *ifop = PRIV(ifofile);



View it on GitLab: https://code.videolan.org/videolan/libdvdread/-/compare/c7f373951bae9642e1ce1fbb2cd02f92c09756e0...482f0ab6de0e3771ed3f573e7107585ba105a6ae

-- 
View it on GitLab: https://code.videolan.org/videolan/libdvdread/-/compare/c7f373951bae9642e1ce1fbb2cd02f92c09756e0...482f0ab6de0e3771ed3f573e7107585ba105a6ae
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the libdvdnav-devel mailing list