[libdvdnav-devel] [Git][videolan/libdvdread][master] ifo: name and document the DVD-Audio still image fields
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Sun Jul 12 19:39:31 UTC 2026
Jean-Baptiste Kempf pushed to branch master at VideoLAN / libdvdread
Commits:
3adb65d1 by Saifelden Mohamed Ismail at 2026-07-12T21:36:17+02:00
ifo: name and document the DVD-Audio still image fields
- - - - -
2 changed files:
- src/dvdread/ifo_types.h
- src/ifo_read.c
Changes:
=====================================
src/dvdread/ifo_types.h
=====================================
@@ -890,10 +890,10 @@ typedef struct {
uint32_t ats_last_sector; /* last sector of ATS_XX_0.BUP*/
uint8_t zero_1[12];
uint32_t atsi_last_sector; /* last sector of ATS_XX_0.IFO*/
- uint16_t specification_version;
+ uint16_t specification_version; /* always 0x0012 */
uint32_t unknown_1;
uint8_t zero_2[90];
- uint32_t atsi_last_byte;
+ uint32_t atsi_last_byte; /* last byte of sector 0, not the end of the file */
uint8_t zero_3[60];
uint32_t vtsm_vobs; /* may be zeros */
uint32_t atst_aobs; /* atst or vtst */
@@ -913,71 +913,89 @@ typedef struct {
#define ATSI_MAT_SIZE 640U
typedef struct {
- uint16_t unknown_1; /* appears to be index, +0x100 for each iter*/
- uint16_t unknown_2; /* either 0x0000 or 0x0100*/
- uint32_t offset_record_table;
+ uint8_t srp_index; /* 1-based, top bit set marks the primary entry and clear the secondary */
+ uint8_t srp_flags; /* varies per entry, purpose not established */
+ uint8_t audio_encode_mode; /* either 0x00, 0x01 or 0x05, meaning not confirmed */
+ uint8_t reserved; /* always 0x00 */
+ uint32_t offset_record_table; /* byte offset of this title's record from the start of the title table */
} ATTRIBUTE_PACKED atsi_title_index_t;
#define ATSI_TITLE_INDEX_SIZE 8U
-/* this will be repeated for each track in each title*/
+/* one per audio program in the title */
typedef struct {
- uint16_t unknown_1; /* will be 0x0000*/
- uint16_t unknown_2; /* will be 0x0000*/
- uint8_t track_number_in_title;
- uint8_t unknown_3; /* will be 0x00*/
- uint32_t first_pts_of_track; /* PTS of the track's first cell; resets per track, not a title-relative timeline */
-
- uint32_t length_pts_of_track; /* track duration in PTS ticks; sums to the title's length_pts */
-
- uint8_t zero[6];
+ uint8_t prog_alloc_flags; /* top bit set on the first program of the title */
+ uint8_t prog_time_attr_flags; /* either 0x00, 0x10 or 0x30, meaning not confirmed */
+ uint16_t reserved_1; /* always 0x0000 */
+ uint8_t track_number_in_title; /* the cell this program starts on, 1-based */
+ uint8_t unknown_3; /* always 0x00 */
+ uint32_t first_pts_of_track; /* start PTS of the program's first cell, not always a title-relative timeline */
+ uint32_t length_pts_of_track; /* length of the program in PTS, the programs sum to length_pts */
+ uint8_t zero[6]; /* will be 0x00 */
} ATTRIBUTE_PACKED atsi_track_timestamp_t;
#define ATSI_TRACK_TIMESTAMP_SIZE 20U
/* this will come after all of the track timstamps, a set of 12byte sector pointer records. One for each track*/
typedef struct {
- uint32_t unknown_1; /* will be 0x01000000*/
+ uint8_t cell_index; /* 1-based, the first cell of a program is 0x01 */
+ uint8_t cell_type; /* 0x00 for an audio cell, other types not seen */
+ uint16_t reserved; /* always 0x0000 */
uint32_t start_sector; /* first sector, relative to the first AOB file */
- uint32_t end_sector; /* last sector, inclusive, relative to the first AOB file */
+ uint32_t end_sector; /* last sector of the cell, relative to the first AOB file */
} ATTRIBUTE_PACKED atsi_track_pointer_t;
#define ATSI_TRACK_POINTER_SIZE 12U
-/* One entry per track. start_value and end_value are byte offsets delimiting the
- * track's records in the still frame table (atsi_asvs_frame_t) that follows. */
-typedef struct {
- uint8_t unknown_1; /* always 0x01 */
- uint8_t unknown_2; /* flag, 0x00 or 0x04 */
- uint16_t start_value; /* first byte offset into the still frame records */
- uint16_t end_value; /* last byte offset into the still frame records */
-} ATTRIBUTE_PACKED atsi_asvs_range_t;
-#define ATSI_ASVS_RANGES_TABLE_SIZE 6U
-
-/* there is one set of these per frame group */
+/**
+ * One entry per audio program. start_value and end_value give the first and
+ * last byte of this program's records in the dlist table that follows. The
+ * first entry starts right after this table, so its start_value equals the
+ * table's byte length, 6 per program.
+ */
typedef struct {
- uint8_t pic_seq; /* picture number within the frame group; restarts at each group */
- uint8_t unknown_1; /* zero? */
- uint16_t unknown_2;
- uint32_t onset_pts; /* onset time (90 kHz) relative to the track start */
- uint16_t unknown_3;
-} ATTRIBUTE_PACKED atsi_asvs_frame_t;
-#define ATSI_ASVS_FRAME_TABLE_SIZE 10U
+ uint8_t asvu_n; /* 1-based ASVU number holding this program's stills, several programs may share one */
+ struct ATTRIBUTE_PACKED {
+ uint8_t order : 2; /* only 0 (sequential) seen */
+ uint8_t timing : 2; /* 0 timed slideshow, 1 user browsable */
+ uint8_t zero : 4;
+ } dmod;
+ uint16_t start_value; /* first byte of this entry */
+ uint16_t end_value; /* last byte of this entry */
+} ATTRIBUTE_PACKED ats_pg_asv_pbi_srp_t;
+#define ATS_PG_ASV_PBI_SRP_SIZE 6U
+/**
+ * One still picture. These records follow the entry table and run up to the
+ * largest end_value in that table. asv_number starts again at 1 for each still
+ * video unit. display_timing holds the picture's MPEG time in slideshow mode
+ * and is 0 in browsable mode.
+ */
typedef struct {
- uint16_t unknown_1; /* will be 0x0000*/
- uint8_t nr_tracks; /* unsure if this holds up for other files*/
- uint8_t nr_pointer_records; /* unsure if this holds up for other files*/
- uint32_t length_pts; /* this is MPEG time, Not DVD time */
-
- uint16_t unknown_3; /* will be 0x0000*/
- uint16_t unknown_4; /* will be 0x0010*/
- uint16_t start_sector_pointers_table; /* pointer to start of sector pointers table, relative to start of title record*/
- uint16_t start_still_pic_table; /* pointer to the still picture table, relative to start of title record; 0 if the title has no stills */
+ uint8_t asv_number; /* the still's number within its unit, restarting at 1 each unit */
+ uint8_t fosl_btnn; /* probably the initial button number, only 0x00 seen */
+ uint8_t unknown_a; /* 0 in slideshow mode, a small often sequential value in browsable mode, maybe a menu page marker */
+ uint8_t track_nr; /* on a shared slideshow, which track the still belongs to, unused when each track has its own stills */
+ uint32_t display_timing; /* when the still is shown, an MPEG PTS, 0 in browsable mode */
+ uint8_t start_transition_mode; /* probably a transition effect, only 0x00 seen */
+ uint8_t termination_transition_mode; /* probably a transition effect, only 0x00 seen */
+} ATTRIBUTE_PACKED asv_dlist_t;
+#define ASV_DLIST_SIZE 10U
+
+typedef struct {
+ /* the address field positions below hold for the common layout, some discs
+ * arrange them differently */
+ uint16_t header_flags; /* only 0x0000 seen */
+ uint8_t nr_tracks; /* number of audio programs in this title */
+ uint8_t nr_pointer_records; /* number of cell records */
+ uint32_t length_pts; /* total play time of the title, MPEG PTS */
+ uint16_t reserved_1; /* always 0x0000 */
+ uint16_t ats_pgit_sa; /* always 0x0010, the low 12 bits look like a byte offset but this is untested */
+ uint16_t start_sector_pointers_table; /* byte offset of the cell info table, from title record start */
+ uint16_t ats_asv_pbit_sa; /* byte offset of the still video table, 0x0000 when the title has no stills */
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. The range rows (one per
- * track) hold byte offsets into the still frame records that follow them. */
- atsi_asvs_range_t *atsi_asvs_range_rows; /* length determined by nr_tracks */
- atsi_asvs_frame_t *atsi_track_still_frame_rows;
+ /* these entries only exist when theres an ASVS */
+ ats_pg_asv_pbi_srp_t *ats_pg_asv_pbi_srp; /* one per audio program */
+ asv_dlist_t *dlist; /* runs up to the largest end_value in the entries above */
} ATTRIBUTE_PACKED atsi_title_record_t;
#define ATSI_TITLE_ROW_TABLE_SIZE 16U
@@ -997,65 +1015,44 @@ typedef struct {
*/
/**
- * ASVS Frame Groupings (unofficial term):
+ * An ASVU groups the still pictures (P_VOBs) that are loaded before the
+ * matching audio program plays. Each P_VOB is one still I-picture.
*
- * 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
+ * ASV_SRPT addressing: with the high bit (0x8000) set the value is an offset
+ * from the parent ASVU ref_start_sector, otherwise it is an absolute offset
+ * from the first P_VOB.
*/
- /**
- * ASVS Group Entry
- * Describes a contiguous sequence of frames in the MPEG stream,
- */
+/* general information for one Audio Still Video Unit */
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 the group's frames in AUDIO_SV.VOB */
-} ATTRIBUTE_PACKED asvs_group_t;
-#define ASVS_GROUP_SIZE 8U
-
-#define ASVS_GROUP_MAX_SIZE 99U
+ uint8_t p_vob_ns; /* number of still pictures in this unit */
+ uint8_t unknown_1;
+ uint16_t start_p_vob_number; /* number of this unit's first still picture, 1-based */
+ uint16_t zero_1;
+ uint16_t ref_start_sector; /* base sector the unit's relative pointers count from */
+} ATTRIBUTE_PACKED asvu_gi_t;
+#define ASVU_GI_SIZE 8U
+#define ASVU_GI_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
+/* search pointer to one still picture, see the addressing note above */
+typedef uint16_t asv_srpt_t;
+#define ASV_SRPT_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 */
+ char asvs_identifier[12]; /* DVDAUDIOASVS */
+ uint16_t asvs_nr_of_asvus; /* number of still video units in asvu_gi */
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;
+ uint16_t zero_1;
+ uint16_t asvu_atr; /* unit attributes, only 0x0002 seen */
+ uint16_t zero_2;
+ uint16_t p_vobs_ea; /* end of the still picture area, in sectors */
+ uint32_t p_vobs_sp_plt[16]; /* sub-picture palette, 16 colours, default 0x00108080 */
+ uint8_t unknown_2[8]; /* trailing 8 bytes, usually more palette words */
+ asvu_gi_t asvu_gi[ASVU_GI_MAX_SIZE]; /* size determined by asvs_nr_of_asvus */
+ asv_srpt_t *asv_srpt; /* length is the sum of p_vob_ns over asvu_gi */
} ATTRIBUTE_PACKED asvs_mat_t;
#define ASVS_MAT_SIZE 888U
=====================================
src/ifo_read.c
=====================================
@@ -745,8 +745,8 @@ void ifoFree_TT(ifo_handle_t *ifofile){
for (int j=0;j<ifofile->atsi_title_table->nr_titles;j++){
free((ifofile->atsi_title_table->atsi_title_row_tables+j)->atsi_track_pointer_rows);
free((ifofile->atsi_title_table->atsi_title_row_tables+j)->atsi_track_timestamp_rows);
- free((ifofile->atsi_title_table->atsi_title_row_tables+j)->atsi_asvs_range_rows);
- free((ifofile->atsi_title_table->atsi_title_row_tables+j)->atsi_track_still_frame_rows);
+ free((ifofile->atsi_title_table->atsi_title_row_tables+j)->ats_pg_asv_pbi_srp);
+ free((ifofile->atsi_title_table->atsi_title_row_tables+j)->dlist);
}
free(ifofile->atsi_title_table->atsi_title_row_tables);
free(ifofile->atsi_title_table->atsi_index_rows);
@@ -821,7 +821,7 @@ void ifoClose(ifo_handle_t *ifofile) {
}
if(ifofile->asvs_mat){
- free(ifofile->asvs_mat->frame_offsets_sectors);
+ free(ifofile->asvs_mat->asv_srpt);
free(ifofile->asvs_mat);
}
@@ -1043,48 +1043,48 @@ static int ifoRead_ASVS(ifo_handle_t *ifofile){
return 0;
}
- B2N_16(asvs_mat->asvs_nr_groups);
+ B2N_16(asvs_mat->asvs_nr_of_asvus);
B2N_16(asvs_mat->specification_version);
- B2N_16(asvs_mat->length_sectors);
+ B2N_16(asvs_mat->p_vobs_ea);
/* a broken disc could ask for more groups than fit */
- if(asvs_mat->asvs_nr_groups > ASVS_GROUP_MAX_SIZE) {
+ if(asvs_mat->asvs_nr_of_asvus > ASVU_GI_MAX_SIZE) {
free(ifofile->asvs_mat);
ifofile->asvs_mat = NULL;
return 0;
}
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].nr_frames;
+ for (int i = 0; i < asvs_mat->asvs_nr_of_asvus; i++ ) {
+ B2N_16(asvs_mat->asvu_gi[i].start_p_vob_number);
+ B2N_16(asvs_mat->asvu_gi[i].ref_start_sector);
+ CHECK_ZERO(asvs_mat->asvu_gi[i].zero_1);
+ total_nr_frames+=asvs_mat->asvu_gi[i].p_vob_ns;
}
- asvs_mat->frame_offsets_sectors = calloc(total_nr_frames, ASVS_FRAME_RECORD_SIZE);
- if(!asvs_mat->frame_offsets_sectors) {
+ asvs_mat->asv_srpt = calloc(total_nr_frames, ASV_SRPT_SIZE);
+ if(!asvs_mat->asv_srpt) {
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);
+ if(!DVDReadBytes(ifop->file, asvs_mat->asv_srpt,
+ total_nr_frames * ASV_SRPT_SIZE )) {
+ free(asvs_mat->asv_srpt);
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]);
+ B2N_16(asvs_mat->asv_srpt[i]);
CHECK_VALUE(asvs_mat->specification_version == 0x0012);
- CHECK_ZERO(asvs_mat->zero1);
- CHECK_ZERO(asvs_mat->zero2);
+ CHECK_ZERO(asvs_mat->zero_1);
+ CHECK_ZERO(asvs_mat->zero_2);
return 1;
}
@@ -1094,22 +1094,22 @@ static void ifoRead_TT_stillpics(struct ifo_handle_private_s *ifop,
atsi_title_record_t *index,
uint32_t record_offset) {
int nr_tracks = index->nr_tracks;
- atsi_asvs_range_t *ranges;
- atsi_asvs_frame_t *frames;
+ ats_pg_asv_pbi_srp_t *ranges;
+ asv_dlist_t *frames;
int base, max_end = 0, nr_frames;
if(nr_tracks <= 0)
return;
- if(!DVDFileSeek_(ifop->file, record_offset + index->start_still_pic_table
+ if(!DVDFileSeek_(ifop->file, record_offset + index->ats_asv_pbit_sa
+ DVD_BLOCK_LEN))
return;
- ranges = calloc(nr_tracks, sizeof(atsi_asvs_range_t));
+ ranges = calloc(nr_tracks, sizeof(ats_pg_asv_pbi_srp_t));
if(!ranges)
return;
- if(!DVDReadBytes(ifop->file, ranges, nr_tracks * sizeof(atsi_asvs_range_t))) {
+ if(!DVDReadBytes(ifop->file, ranges, nr_tracks * sizeof(ats_pg_asv_pbi_srp_t))) {
free(ranges);
return;
}
@@ -1120,31 +1120,29 @@ static void ifoRead_TT_stillpics(struct ifo_handle_private_s *ifop,
if(ranges[j].end_value > max_end)
max_end = ranges[j].end_value;
}
- index->atsi_asvs_range_rows = ranges;
+ index->ats_pg_asv_pbi_srp = ranges;
/* The still frame records follow the range table; end_values are byte
* offsets into them, so the last referenced record bounds their count. */
- base = nr_tracks * ATSI_ASVS_RANGES_TABLE_SIZE;
+ base = nr_tracks * ATS_PG_ASV_PBI_SRP_SIZE;
if(max_end < base)
return;
- nr_frames = (max_end - base) / ATSI_ASVS_FRAME_TABLE_SIZE + 1;
+ nr_frames = (max_end - base) / ASV_DLIST_SIZE + 1;
- frames = calloc(nr_frames, sizeof(atsi_asvs_frame_t));
+ frames = calloc(nr_frames, sizeof(asv_dlist_t));
if(!frames)
return;
- if(!DVDReadBytes(ifop->file, frames, nr_frames * sizeof(atsi_asvs_frame_t))) {
+ if(!DVDReadBytes(ifop->file, frames, nr_frames * sizeof(asv_dlist_t))) {
free(frames);
return;
}
for(int j = 0; j < nr_frames; j++) {
- B2N_16(frames[j].unknown_2);
- B2N_32(frames[j].onset_pts);
- B2N_16(frames[j].unknown_3);
+ B2N_32(frames[j].display_timing);
}
- index->atsi_track_still_frame_rows = frames;
+ index->dlist = frames;
}
int ifoRead_TT(ifo_handle_t *ifofile) {
@@ -1215,7 +1213,7 @@ int ifoRead_TT(ifo_handle_t *ifofile) {
goto fail_audio;
B2N_16(index->start_sector_pointers_table);
- B2N_16(index->start_still_pic_table);
+ B2N_16(index->ats_asv_pbit_sa);
B2N_32(index->length_pts);
int nr_tracks=index->nr_tracks;
int nr_pointer_records = index->nr_pointer_records;
@@ -1264,7 +1262,7 @@ int ifoRead_TT(ifo_handle_t *ifofile) {
== ( ATSI_TITLE_ROW_TABLE_SIZE
+ index->nr_tracks * ATSI_TRACK_TIMESTAMP_SIZE ) );
- if(index->start_still_pic_table)
+ if(index->ats_asv_pbit_sa)
ifoRead_TT_stillpics(ifop, index, record_offset);
}
@@ -1274,8 +1272,8 @@ int ifoRead_TT(ifo_handle_t *ifofile) {
for (int j = 0; j < i; j++){
free((atsi_title_table->atsi_title_row_tables + j)->atsi_track_pointer_rows);
free((atsi_title_table->atsi_title_row_tables + j)->atsi_track_timestamp_rows);
- free((atsi_title_table->atsi_title_row_tables + j)->atsi_asvs_range_rows);
- free((atsi_title_table->atsi_title_row_tables + j)->atsi_track_still_frame_rows);
+ free((atsi_title_table->atsi_title_row_tables + j)->ats_pg_asv_pbi_srp);
+ free((atsi_title_table->atsi_title_row_tables + j)->dlist);
}
free(atsi_title_table->atsi_title_row_tables);
free(ifofile->atsi_title_table->atsi_index_rows);
View it on GitLab: https://code.videolan.org/videolan/libdvdread/-/commit/3adb65d137ec3cb9bb48c0a5c88be2895c32a5ab
--
View it on GitLab: https://code.videolan.org/videolan/libdvdread/-/commit/3adb65d137ec3cb9bb48c0a5c88be2895c32a5ab
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the libdvdnav-devel
mailing list