[libdvdnav-devel] [Git][videolan/libdvdread][master] 3 commits: ifoRead_ASVS: size the frame table from nr_frames
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Sun Jul 12 19:32:20 UTC 2026
Jean-Baptiste Kempf pushed to branch master at VideoLAN / libdvdread
Commits:
0d5b6530 by Kacper Michajłow at 2026-07-12T21:26:33+02:00
ifoRead_ASVS: size the frame table from nr_frames
The ASVS frame offset table length was computed by summing each group's
start_sector instead of its nr_frames, massively over-reading the IFO.
- - - - -
e9dbc9f3 by Kacper Michajłow at 2026-07-12T21:26:33+02:00
ifoRead_TT: parse the ASVS still picture table
The per-title still picture table that atsi_title_record_t declares was
never read. Parse it into the declared atsi_asvs_range_rows and
atsi_track_still_frame_rows when a title has stills, locating it via the
title record pointer previously named unknown_5 (renamed to
start_still_pic_table now that its meaning is known). The number of still
frame records is derived from the range rows' end offsets.
- - - - -
79da58a7 by Kacper Michajłow at 2026-07-12T21:26:33+02:00
ifo: name and document DVD-Audio ATSI and ASVS fields
Name the still-picture sequence and onset fields, clarify the track
timestamp, sector pointer and ASVS group comments, and fix an
atsi_title_index_t field name typo.
- - - - -
2 changed files:
- src/dvdread/ifo_types.h
- src/ifo_read.c
Changes:
=====================================
src/dvdread/ifo_types.h
=====================================
@@ -914,8 +914,8 @@ typedef struct {
typedef struct {
uint16_t unknown_1; /* appears to be index, +0x100 for each iter*/
- uint16_t unkown_2; /* either 0x0000 or 0x0100*/
- uint32_t offset_record_table;
+ uint16_t unknown_2; /* either 0x0000 or 0x0100*/
+ uint32_t offset_record_table;
} ATTRIBUTE_PACKED atsi_title_index_t;
#define ATSI_TITLE_INDEX_SIZE 8U
@@ -925,9 +925,9 @@ typedef struct {
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; /* this is MPEG time, Not DVD time */
+ 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; /* this is MPEG time, Not DVD time */
+ uint32_t length_pts_of_track; /* track duration in PTS ticks; sums to the title's length_pts */
uint8_t zero[6];
} ATTRIBUTE_PACKED atsi_track_timestamp_t;
@@ -936,29 +936,28 @@ typedef struct {
/* 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*/
- uint32_t start_sector; /* relative to first AOB file*/
- uint32_t end_sector; /* relative to first end of AOB file */
+ 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 */
} 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 */
+/* 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; /* 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;
+ 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, resets at 01 when at start of group */
+/* there is one set of these per frame group */
typedef struct {
- uint8_t frame_index; /* overall frame index? */
+ 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;
- uint16_t unknown_4;
- uint16_t unknown_5;
} ATTRIBUTE_PACKED atsi_asvs_frame_t;
#define ATSI_ASVS_FRAME_TABLE_SIZE 10U
@@ -971,13 +970,14 @@ typedef struct {
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 unknown_5; /* will be 0x0000*/
+ 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 */
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 */
+ /* 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; /* length determined by the ASVS struct, total nr of frames */
+ atsi_asvs_frame_t *atsi_track_still_frame_rows;
} ATTRIBUTE_PACKED atsi_title_record_t;
#define ATSI_TITLE_ROW_TABLE_SIZE 16U
@@ -1027,7 +1027,7 @@ typedef struct {
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 */
+ 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
=====================================
src/ifo_read.c
=====================================
@@ -745,6 +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);
free(ifofile->atsi_title_table->atsi_index_rows);
@@ -1057,7 +1059,7 @@ static int ifoRead_ASVS(ifo_handle_t *ifofile){
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;
+ total_nr_frames+=asvs_mat->asvs_groups[i].nr_frames;
}
@@ -1087,6 +1089,64 @@ static int ifoRead_ASVS(ifo_handle_t *ifofile){
}
+/* Read the still picture table (ASVS) of an audio title, if present. */
+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;
+ int base, max_end = 0, nr_frames;
+
+ if(nr_tracks <= 0)
+ return;
+
+ if(!DVDFileSeek_(ifop->file, record_offset + index->start_still_pic_table
+ + DVD_BLOCK_LEN))
+ return;
+
+ ranges = calloc(nr_tracks, sizeof(atsi_asvs_range_t));
+ if(!ranges)
+ return;
+
+ if(!DVDReadBytes(ifop->file, ranges, nr_tracks * sizeof(atsi_asvs_range_t))) {
+ free(ranges);
+ return;
+ }
+
+ for(int j = 0; j < nr_tracks; j++) {
+ B2N_16(ranges[j].start_value);
+ B2N_16(ranges[j].end_value);
+ if(ranges[j].end_value > max_end)
+ max_end = ranges[j].end_value;
+ }
+ index->atsi_asvs_range_rows = 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;
+ if(max_end < base)
+ return;
+ nr_frames = (max_end - base) / ATSI_ASVS_FRAME_TABLE_SIZE + 1;
+
+ frames = calloc(nr_frames, sizeof(atsi_asvs_frame_t));
+ if(!frames)
+ return;
+
+ if(!DVDReadBytes(ifop->file, frames, nr_frames * sizeof(atsi_asvs_frame_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);
+ }
+
+ index->atsi_track_still_frame_rows = frames;
+}
+
int ifoRead_TT(ifo_handle_t *ifofile) {
struct ifo_handle_private_s *ifop = PRIV(ifofile);
@@ -1155,6 +1215,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_32(index->length_pts);
int nr_tracks=index->nr_tracks;
int nr_pointer_records = index->nr_pointer_records;
@@ -1203,6 +1264,9 @@ 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)
+ ifoRead_TT_stillpics(ifop, index, record_offset);
+
}
return 1;
@@ -1210,6 +1274,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);
free(ifofile->atsi_title_table->atsi_index_rows);
View it on GitLab: https://code.videolan.org/videolan/libdvdread/-/compare/17f1f079e0dbf49804b2f4b426b056a65aca4270...79da58a7193d8011669838e70f99842d1aec09f1
--
View it on GitLab: https://code.videolan.org/videolan/libdvdread/-/compare/17f1f079e0dbf49804b2f4b426b056a65aca4270...79da58a7193d8011669838e70f99842d1aec09f1
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