#include "dvdnav/dvdnav.h"
#include "dvdread/ifo_read.h"
+#include "dvdread/ifo_print.h"
#include "dvdread/nav_read.h"
#define DVD_READ_CACHE 1
static hb_dvd_t * hb_dvdnav_init( char * path );
static int hb_dvdnav_title_count( hb_dvd_t * d );
static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * d, int t );
-static int hb_dvdnav_start( hb_dvd_t * d, int title, int chapter );
+static int hb_dvdnav_start( hb_dvd_t * d, hb_title_t *title, int chapter );
static void hb_dvdnav_stop( hb_dvd_t * d );
static int hb_dvdnav_seek( hb_dvd_t * d, float f );
static int hb_dvdnav_read( hb_dvd_t * d, hb_buffer_t * b );
static int hb_dvdnav_chapter( hb_dvd_t * d );
static void hb_dvdnav_close( hb_dvd_t ** _d );
static int hb_dvdnav_angle_count( hb_dvd_t * d );
-static void hb_dvdnav_set_angle( hb_dvd_t * e, int angle );
+static void hb_dvdnav_set_angle( hb_dvd_t * d, int angle );
+static int hb_dvdnav_main_feature( hb_dvd_t * d, hb_list_t * list_title );
hb_dvd_func_t hb_dvdnav_func =
{
hb_dvdnav_read,
hb_dvdnav_chapter,
hb_dvdnav_angle_count,
- hb_dvdnav_set_angle
+ hb_dvdnav_set_angle,
+ hb_dvdnav_main_feature
};
+// there can be at most 999 PGCs per title. round that up to the nearest
+// power of two.
+#define MAX_PGCN 1024
+
/***********************************************************************
* Local prototypes
**********************************************************************/
+static void PgcWalkInit( uint32_t pgcn_map[MAX_PGCN/32] );
+static int FindChapterIndex( hb_list_t * list, int pgcn, int pgn );
+static int NextPgcn( ifo_handle_t *ifo, int pgcn, uint32_t pgcn_map[MAX_PGCN/32] );
static int FindNextCell( pgc_t *pgc, int cell_cur );
static int dvdtime2msec( dvd_time_t * );
-//static int hb_dvdnav_is_break( hb_dvdnav_t * d );
hb_dvd_func_t * hb_dvdnav_methods( void )
{
}
/***********************************************************************
+ * hb_dvdnav_reset
+ ***********************************************************************
+ * Once dvdnav has entered the 'stopped' state, it can not be revived
+ * dvdnav_reset doesn't work because it doesn't remember the path
+ * So this function re-opens dvdnav
+ **********************************************************************/
+static int hb_dvdnav_reset( hb_dvdnav_t * d )
+{
+ if ( d->dvdnav )
+ dvdnav_close( d->dvdnav );
+
+ /* Open device */
+ if( dvdnav_open(&d->dvdnav, d->path) != DVDNAV_STATUS_OK )
+ {
+ /*
+ * Not an error, may be a stream - which we'll try in a moment.
+ */
+ hb_log( "dvd: not a dvd - trying as a stream/file instead" );
+ goto fail;
+ }
+
+ if (dvdnav_set_readahead_flag(d->dvdnav, DVD_READ_CACHE) !=
+ DVDNAV_STATUS_OK)
+ {
+ hb_error("Error: dvdnav_set_readahead_flag: %s\n",
+ dvdnav_err_to_string(d->dvdnav));
+ goto fail;
+ }
+
+ /*
+ ** set the PGC positioning flag to have position information
+ ** relatively to the whole feature instead of just relatively to the
+ ** current chapter
+ **/
+ if (dvdnav_set_PGC_positioning_flag(d->dvdnav, 1) != DVDNAV_STATUS_OK)
+ {
+ hb_error("Error: dvdnav_set_PGC_positioning_flag: %s\n",
+ dvdnav_err_to_string(d->dvdnav));
+ goto fail;
+ }
+ return 1;
+
+fail:
+ if( d->dvdnav ) dvdnav_close( d->dvdnav );
+ return 0;
+}
+
+/***********************************************************************
* hb_dvdnav_init
***********************************************************************
*
{
hb_dvd_t * e;
hb_dvdnav_t * d;
+ int region_mask;
e = calloc( sizeof( hb_dvd_t ), 1 );
d = &(e->dvdnav);
+ /* Log DVD drive region code */
+ if ( hb_dvd_region( path, ®ion_mask ) == 0 )
+ {
+ hb_log( "dvd: Region mask 0x%02x", region_mask );
+ if ( region_mask == 0xFF )
+ {
+ hb_log( "dvd: Warning, DVD device has no region set" );
+ }
+ }
+
/* Open device */
if( dvdnav_open(&d->dvdnav, path) != DVDNAV_STATUS_OK )
{
return titles;
}
+static uint64_t
+PttDuration(ifo_handle_t *ifo, int ttn, int pttn, int *blocks, int *last_pgcn)
+{
+ int pgcn, pgn;
+ pgc_t * pgc;
+ uint64_t duration = 0;
+ int cell_start, cell_end;
+ int i;
+
+ // Initialize map of visited pgc's to prevent loops
+ uint32_t pgcn_map[MAX_PGCN/32];
+ PgcWalkInit( pgcn_map );
+ pgcn = ifo->vts_ptt_srpt->title[ttn-1].ptt[pttn-1].pgcn;
+ pgn = ifo->vts_ptt_srpt->title[ttn-1].ptt[pttn-1].pgn;
+ if ( pgcn < 1 || pgcn > ifo->vts_pgcit->nr_of_pgci_srp || pgcn >= MAX_PGCN)
+ {
+ hb_error( "invalid PGC ID %d, skipping", pgcn );
+ return 0;
+ }
+
+ if( pgn <= 0 || pgn > 99 )
+ {
+ hb_error( "scan: pgn %d not valid, skipping", pgn );
+ return 0;
+ }
+
+ *blocks = 0;
+ do
+ {
+ pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc;
+ if (!pgc)
+ {
+ hb_error( "scan: pgc not valid, skipping" );
+ break;
+ }
+ if (pgn > pgc->nr_of_programs)
+ {
+ pgn = 1;
+ continue;
+ }
+
+ duration += 90LL * dvdtime2msec( &pgc->playback_time );
+
+ cell_start = pgc->program_map[pgn-1] - 1;
+ cell_end = pgc->nr_of_cells - 1;
+ for(i = cell_start; i <= cell_end; i = FindNextCell(pgc, i))
+ {
+ *blocks += pgc->cell_playback[i].last_sector + 1 -
+ pgc->cell_playback[i].first_sector;
+ }
+ *last_pgcn = pgcn;
+ pgn = 1;
+ } while((pgcn = NextPgcn(ifo, pgcn, pgcn_map)) != 0);
+ return duration;
+}
+
/***********************************************************************
* hb_dvdnav_title_scan
**********************************************************************/
hb_dvdnav_t * d = &(e->dvdnav);
hb_title_t * title;
- ifo_handle_t * vts = NULL;
- int pgc_id, pgn, i;
+ ifo_handle_t * ifo = NULL;
+ int pgcn, pgn, pgcn_end, i, c;
+ int title_pgcn;
pgc_t * pgc;
int cell_cur;
hb_chapter_t * chapter;
- int c;
- uint64_t duration;
+ int count;
+ uint64_t duration, longest;
+ int longest_pgcn, longest_pgn, longest_pgcn_end;
float duration_correction;
const char * name;
hb_log( "scan: scanning title %d", t );
title = hb_title_init( d->path, t );
+ title->type = HB_DVD_TYPE;
if (dvdnav_get_title_string(d->dvdnav, &name) == DVDNAV_STATUS_OK)
{
strncpy( title->name, name, sizeof( title->name ) );
}
hb_log( "scan: opening IFO for VTS %d", title->vts );
- if( !( vts = ifoOpen( d->reader, title->vts ) ) )
+ if( !( ifo = ifoOpen( d->reader, title->vts ) ) )
{
hb_error( "scan: ifoOpen failed" );
goto fail;
/* ignore titles with bogus cell addresses so we don't abort later
** in libdvdread. */
- for ( i = 0; i < vts->vts_c_adt->nr_of_vobs; ++i)
+ for ( i = 0; i < ifo->vts_c_adt->nr_of_vobs; ++i)
{
- if( (vts->vts_c_adt->cell_adr_table[i].start_sector & 0xffffff ) ==
+ if( (ifo->vts_c_adt->cell_adr_table[i].start_sector & 0xffffff ) ==
0xffffff )
{
hb_error( "scan: cell_adr_table[%d].start_sector invalid (0x%x) "
"- skipping title", i,
- vts->vts_c_adt->cell_adr_table[i].start_sector );
+ ifo->vts_c_adt->cell_adr_table[i].start_sector );
goto fail;
}
- if( (vts->vts_c_adt->cell_adr_table[i].last_sector & 0xffffff ) ==
+ if( (ifo->vts_c_adt->cell_adr_table[i].last_sector & 0xffffff ) ==
0xffffff )
{
hb_error( "scan: cell_adr_table[%d].last_sector invalid (0x%x) "
"- skipping title", i,
- vts->vts_c_adt->cell_adr_table[i].last_sector );
+ ifo->vts_c_adt->cell_adr_table[i].last_sector );
goto fail;
}
- if( vts->vts_c_adt->cell_adr_table[i].start_sector >=
- vts->vts_c_adt->cell_adr_table[i].last_sector )
+ if( ifo->vts_c_adt->cell_adr_table[i].start_sector >=
+ ifo->vts_c_adt->cell_adr_table[i].last_sector )
{
hb_error( "scan: cell_adr_table[%d].start_sector (0x%x) "
"is not before last_sector (0x%x) - skipping title", i,
- vts->vts_c_adt->cell_adr_table[i].start_sector,
- vts->vts_c_adt->cell_adr_table[i].last_sector );
+ ifo->vts_c_adt->cell_adr_table[i].start_sector,
+ ifo->vts_c_adt->cell_adr_table[i].last_sector );
goto fail;
}
}
/* Position of the title in the VTS */
title->ttn = d->vmg->tt_srpt->title[t-1].vts_ttn;
- if ( title->ttn < 1 || title->ttn > vts->vts_ptt_srpt->nr_of_srpts )
+ if ( title->ttn < 1 || title->ttn > ifo->vts_ptt_srpt->nr_of_srpts )
{
hb_error( "invalid VTS PTT offset %d for title %d, skipping", title->ttn, t );
goto fail;
}
+ longest = 0LL;
+ longest_pgcn = -1;
+ longest_pgn = 1;
+ longest_pgcn_end = -1;
+ pgcn_end = -1;
+ for( i = 0; i < ifo->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts; i++ )
+ {
+ int blocks = 0;
- /* Get pgc */
- pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgcn;
- if ( pgc_id < 1 || pgc_id > vts->vts_pgcit->nr_of_pgci_srp )
+ duration = PttDuration(ifo, title->ttn, i+1, &blocks, &pgcn_end);
+ pgcn = ifo->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgcn;
+ pgn = ifo->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgn;
+ if( duration > longest )
+ {
+ longest_pgcn = pgcn;
+ longest_pgn = pgn;
+ longest_pgcn_end = pgcn_end;
+ longest = duration;
+ title->block_count = blocks;
+ }
+ else if (pgcn == longest_pgcn && pgn < longest_pgn)
+ {
+ longest_pgn = pgn;
+ title->block_count = blocks;
+ }
+ }
+
+ /* ignore titles under 10 seconds because they're often stills or
+ * clips with no audio & our preview code doesn't currently handle
+ * either of these. */
+ if( longest < 900000LL )
{
- hb_error( "invalid PGC ID %d for title %d, skipping", pgc_id, t );
+ hb_log( "scan: ignoring title (too short)" );
goto fail;
}
- pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgn;
- pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc;
- hb_log("pgc_id: %d, pgn: %d: pgc: 0x%x", pgc_id, pgn, pgc);
+ pgcn = longest_pgcn;
+ pgcn_end = longest_pgcn_end;
+ pgn = longest_pgn;;
+ title_pgcn = pgcn;
+
- if( !pgc )
+ /* Get pgc */
+ if ( pgcn < 1 || pgcn > ifo->vts_pgcit->nr_of_pgci_srp || pgcn >= MAX_PGCN)
{
- hb_error( "scan: pgc not valid, skipping" );
+ hb_error( "invalid PGC ID %d for title %d, skipping", pgcn, t );
goto fail;
}
- if( pgn <= 0 || pgn > 99 )
+ pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc;
+
+ hb_log("pgc_id: %d, pgn: %d: pgc: %p", pgcn, pgn, pgc);
+ if (pgn > pgc->nr_of_programs)
{
- hb_error( "scan: pgn %d not valid, skipping", pgn );
+ hb_error( "invalid PGN %d for title %d, skipping", pgn, t );
goto fail;
}
- /* Start cell */
- title->cell_start = pgc->program_map[pgn-1] - 1;
+ /* Title start */
+ title->cell_start = pgc->program_map[pgn-1] - 1;
title->block_start = pgc->cell_playback[title->cell_start].first_sector;
- /* End cell */
- title->cell_end = pgc->nr_of_cells - 1;
- title->block_end = pgc->cell_playback[title->cell_end].last_sector;
+ pgc = ifo->vts_pgcit->pgci_srp[pgcn_end-1].pgc;
- /* Block count */
- title->block_count = 0;
- cell_cur = title->cell_start;
- while( cell_cur <= title->cell_end )
- {
-#define cp pgc->cell_playback[cell_cur]
- title->block_count += cp.last_sector + 1 - cp.first_sector;
-#undef cp
- cell_cur = FindNextCell( pgc, cell_cur );
- }
+ /* Title end */
+ title->cell_end = pgc->nr_of_cells - 1;
+ title->block_end = pgc->cell_playback[title->cell_end].last_sector;
hb_log( "scan: vts=%d, ttn=%d, cells=%d->%d, blocks=%d->%d, "
"%d blocks", title->vts, title->ttn, title->cell_start,
title->block_count );
/* Get duration */
- title->duration = 90LL * dvdtime2msec( &pgc->playback_time );
+ title->duration = longest;
title->hours = title->duration / 90000 / 3600;
title->minutes = ( ( title->duration / 90000 ) % 3600 ) / 60;
title->seconds = ( title->duration / 90000 ) % 60;
- hb_log( "scan: duration is %02d:%02d:%02d (%lld ms)",
+ hb_log( "scan: duration is %02d:%02d:%02d (%"PRId64" ms)",
title->hours, title->minutes, title->seconds,
title->duration / 90 );
- /* ignore titles under 10 seconds because they're often stills or
- * clips with no audio & our preview code doesn't currently handle
- * either of these. */
- if( title->duration < 900000LL )
- {
- hb_log( "scan: ignoring title (too short)" );
- goto fail;
- }
-
/* Detect languages */
- for( i = 0; i < vts->vtsi_mat->nr_of_vts_audio_streams; i++ )
+ for( i = 0; i < ifo->vtsi_mat->nr_of_vts_audio_streams; i++ )
{
hb_audio_t * audio, * audio_tmp;
int audio_format, lang_code, audio_control,
audio = calloc( sizeof( hb_audio_t ), 1 );
- audio_format = vts->vtsi_mat->vts_audio_attr[i].audio_format;
- lang_code = vts->vtsi_mat->vts_audio_attr[i].lang_code;
- lang_extension = vts->vtsi_mat->vts_audio_attr[i].code_extension;
+ audio_format = ifo->vtsi_mat->vts_audio_attr[i].audio_format;
+ lang_code = ifo->vtsi_mat->vts_audio_attr[i].lang_code;
+ lang_extension = ifo->vtsi_mat->vts_audio_attr[i].code_extension;
audio_control =
- vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->audio_control[i];
+ ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->audio_control[i];
if( !( audio_control & 0x8000 ) )
{
audio->config.lang.type = lang_extension;
- lang = lang_for_code( vts->vtsi_mat->vts_audio_attr[i].lang_code );
+ lang = lang_for_code( ifo->vtsi_mat->vts_audio_attr[i].lang_code );
snprintf( audio->config.lang.description, sizeof( audio->config.lang.description ), "%s (%s)",
strlen(lang->native_name) ? lang->native_name : lang->eng_name,
hb_list_add( title->list_audio, audio );
}
- if( !hb_list_count( title->list_audio ) )
- {
- hb_log( "scan: ignoring title (no audio track)" );
- goto fail;
- }
-
- memcpy( title->palette,
- vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->palette,
- 16 * sizeof( uint32_t ) );
-
/* Check for subtitles */
- for( i = 0; i < vts->vtsi_mat->nr_of_vts_subp_streams; i++ )
+ for( i = 0; i < ifo->vtsi_mat->nr_of_vts_subp_streams; i++ )
{
hb_subtitle_t * subtitle;
int spu_control;
hb_log( "scan: checking subtitle %d", i + 1 );
spu_control =
- vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->subp_control[i];
+ ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->subp_control[i];
if( !( spu_control & 0x80000000 ) )
{
continue;
}
- if( vts->vtsi_mat->vts_video_attr.display_aspect_ratio )
+ if( ifo->vtsi_mat->vts_video_attr.display_aspect_ratio )
{
- switch( vts->vtsi_mat->vts_video_attr.permitted_df )
+ switch( ifo->vtsi_mat->vts_video_attr.permitted_df )
{
case 1:
position = spu_control & 0xFF;
position = ( spu_control >> 24 ) & 0x7F;
}
- lang_extension = vts->vtsi_mat->vts_subp_attr[i].code_extension;
+ lang_extension = ifo->vtsi_mat->vts_subp_attr[i].code_extension;
- lang = lang_for_code( vts->vtsi_mat->vts_subp_attr[i].lang_code );
+ lang = lang_for_code( ifo->vtsi_mat->vts_subp_attr[i].lang_code );
subtitle = calloc( sizeof( hb_subtitle_t ), 1 );
+ subtitle->track = i+1;
subtitle->id = ( ( 0x20 + position ) << 8 ) | 0xbd;
snprintf( subtitle->lang, sizeof( subtitle->lang ), "%s",
strlen(lang->native_name) ? lang->native_name : lang->eng_name);
snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s",
lang->iso639_2);
+ subtitle->format = PICTURESUB;
+ subtitle->source = VOBSUB;
+ subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB.
subtitle->type = lang_extension;
+
+ memcpy( subtitle->palette,
+ ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->palette,
+ 16 * sizeof( uint32_t ) );
switch( lang_extension )
{
}
/* Chapters */
- hb_log( "scan: title %d has %d chapters", t,
- vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts );
- for( i = 0, c = 1;
- i < vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts; i++ )
+ uint32_t pgcn_map[MAX_PGCN/32];
+ PgcWalkInit( pgcn_map );
+ c = 0;
+ do
{
- int pgc_id_next, pgn_next;
- pgc_t * pgc_next;
+ pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc;
- chapter = calloc( sizeof( hb_chapter_t ), 1 );
- /* remember the on-disc chapter number */
- chapter->index = i + 1;
+ for (i = pgn; i <= pgc->nr_of_programs; i++)
+ {
+ chapter = calloc( sizeof( hb_chapter_t ), 1 );
+
+ chapter->index = c + 1;
+ chapter->pgcn = pgcn;
+ chapter->pgn = i;
+ hb_list_add( title->list_chapter, chapter );
+ c++;
+ }
+
+ pgn = 1;
+ } while ((pgcn = NextPgcn(ifo, pgcn, pgcn_map)) != 0);
+
+ hb_log( "scan: title %d has %d chapters", t, c );
+
+ duration = 0;
+ count = hb_list_count( title->list_chapter );
+ for (i = 0; i < count; i++)
+ {
+ chapter = hb_list_item( title->list_chapter, i );
- pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgcn;
- pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgn;
- pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc;
+ pgcn = chapter->pgcn;
+ pgn = chapter->pgn;
+ pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc;
/* Start cell */
chapter->cell_start = pgc->program_map[pgn-1] - 1;
- chapter->block_start =
- pgc->cell_playback[chapter->cell_start].first_sector;
-
- /* End cell */
- if( i != vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts - 1 )
+ chapter->block_start = pgc->cell_playback[chapter->cell_start].first_sector;
+ // if there are no more programs in this pgc, the end cell is the
+ // last cell. Otherwise it's the cell before the start cell of the
+ // next program.
+ if ( pgn == pgc->nr_of_programs )
{
- /* The cell before the starting cell of the next chapter,
- or... */
- pgc_id_next = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i+1].pgcn;
- pgn_next = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i+1].pgn;
- pgc_next = vts->vts_pgcit->pgci_srp[pgc_id_next-1].pgc;
- chapter->cell_end = pgc_next->program_map[pgn_next-1] - 2;
- if( chapter->cell_end < 0 )
- {
- /* Huh? */
- free( chapter );
- continue;
- }
+ chapter->cell_end = pgc->nr_of_cells - 1;
}
else
{
- /* ... the last cell of the title */
- chapter->cell_end = title->cell_end;
+ chapter->cell_end = pgc->program_map[pgn] - 2;;
}
- chapter->block_end =
- pgc->cell_playback[chapter->cell_end].last_sector;
+ chapter->block_end = pgc->cell_playback[chapter->cell_end].last_sector;
/* Block count, duration */
- pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgcn;
- pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgn;
- pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc;
chapter->block_count = 0;
chapter->duration = 0;
#undef cp
cell_cur = FindNextCell( pgc, cell_cur );
}
- hb_list_add( title->list_chapter, chapter );
- c++;
+ duration += chapter->duration;
}
/* The durations we get for chapters aren't precise. Scale them so
the total matches the title duration */
- duration = 0;
- for( i = 0; i < hb_list_count( title->list_chapter ); i++ )
- {
- chapter = hb_list_item( title->list_chapter, i );
- duration += chapter->duration;
- }
duration_correction = (float) title->duration / (float) duration;
for( i = 0; i < hb_list_count( title->list_chapter ); i++ )
{
chapter->minutes = ( seconds % 3600 ) / 60;
chapter->seconds = seconds % 60;
- hb_log( "scan: chap %d c=%d->%d, b=%d->%d (%d), %lld ms",
+ hb_log( "scan: chap %d c=%d->%d, b=%d->%d (%d), %"PRId64" ms",
chapter->index, chapter->cell_start, chapter->cell_end,
chapter->block_start, chapter->block_end,
chapter->block_count, chapter->duration / 90 );
/* Get aspect. We don't get width/height/rate infos here as
they tend to be wrong */
- switch( vts->vtsi_mat->vts_video_attr.display_aspect_ratio )
+ switch( ifo->vtsi_mat->vts_video_attr.display_aspect_ratio )
{
case 0:
title->container_aspect = 4. / 3.;
title = NULL;
cleanup:
- if( vts ) ifoClose( vts );
+ if( ifo ) ifoClose( ifo );
return title;
}
/***********************************************************************
- * hb_dvdnav_start
- ***********************************************************************
- * Title and chapter start at 1
+ * hb_dvdnav_title_scan
**********************************************************************/
-static int hb_dvdnav_start( hb_dvd_t * e, int title, int chapter )
+static int find_title( hb_list_t * list_title, int title )
+{
+ int ii;
+
+ for ( ii = 0; ii < hb_list_count( list_title ); ii++ )
+ {
+ hb_title_t * hbtitle = hb_list_item( list_title, ii );
+ if ( hbtitle->index == title )
+ return ii;
+ }
+ return -1;
+}
+
+static void skip_some( dvdnav_t * dvdnav, int blocks )
+{
+ int ii;
+ int result, event, len;
+ uint8_t buf[HB_DVD_READ_BUFFER_SIZE];
+
+ for ( ii = 0; ii < blocks; ii++ )
+ {
+ result = dvdnav_get_next_block( dvdnav, buf, &event, &len );
+ if ( result == DVDNAV_STATUS_ERR )
+ {
+ hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(dvdnav));
+ return;
+ }
+ switch ( event )
+ {
+ case DVDNAV_BLOCK_OK:
+ break;
+
+ case DVDNAV_CELL_CHANGE:
+ {
+ } break;
+
+ case DVDNAV_STILL_FRAME:
+ dvdnav_still_skip( dvdnav );
+ break;
+
+ case DVDNAV_WAIT:
+ dvdnav_wait_skip( dvdnav );
+ break;
+
+ case DVDNAV_STOP:
+ return;
+
+ case DVDNAV_HOP_CHANNEL:
+ break;
+
+ case DVDNAV_NAV_PACKET:
+ break;
+
+ case DVDNAV_VTS_CHANGE:
+ {
+ dvdnav_vts_change_event_t *event;
+ event = (dvdnav_vts_change_event_t*)buf;
+ // Some discs initialize the vts with the "first play" item
+ // and some don't seem to. So if we see it is uninitialized,
+ // set it.
+ if ( event->new_vtsN <= 0 )
+ result = dvdnav_title_play( dvdnav, 1 );
+ } break;
+
+ case DVDNAV_HIGHLIGHT:
+ break;
+
+ case DVDNAV_AUDIO_STREAM_CHANGE:
+ break;
+
+ case DVDNAV_SPU_STREAM_CHANGE:
+ break;
+
+ case DVDNAV_SPU_CLUT_CHANGE:
+ break;
+
+ case DVDNAV_NOP:
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static int try_button( dvdnav_t * dvdnav, int menu_id, int button, hb_list_t * list_title )
+{
+ int result, event, len;
+ uint8_t buf[HB_DVD_READ_BUFFER_SIZE];
+ int ii, jj;
+ int32_t cur_title, title, pgcn, pgn;
+ uint64_t longest_duration = 0;
+ int longest = -1;
+
+ pci_t *pci = dvdnav_get_current_nav_pci( dvdnav );
+ result = dvdnav_button_select_and_activate( dvdnav, pci, button + 1 );
+ if (result != DVDNAV_STATUS_OK)
+ {
+ hb_log("dvdnav_button_select_and_activate: %s", dvdnav_err_to_string(dvdnav));
+ }
+
+ result = dvdnav_current_title_program( dvdnav, &title, &pgcn, &pgn );
+ if (result != DVDNAV_STATUS_OK)
+ hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(dvdnav));
+
+ if ( title > 0 )
+ {
+ hb_title_t * hbtitle;
+ int index;
+ index = find_title( list_title, title );
+ hbtitle = hb_list_item( list_title, index );
+ if ( hbtitle != NULL )
+ {
+ if ( hbtitle->duration > longest_duration )
+ {
+ longest_duration = hbtitle->duration;
+ longest = title;
+ }
+ }
+ // If the duration is longer than 10min, assume
+ // this isn't garbage leading to something bigger.
+ if ( longest_duration / 90000 > 10 * 60 )
+ {
+ return longest;
+ }
+ }
+ cur_title = title;
+
+ for (jj = 0; jj < 5; jj++)
+ {
+ for (ii = 0; ii < 2000; ii++)
+ {
+ result = dvdnav_get_next_block( dvdnav, buf, &event, &len );
+ if ( result == DVDNAV_STATUS_ERR )
+ {
+ hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(dvdnav));
+ return 0;
+ }
+ switch ( event )
+ {
+ case DVDNAV_BLOCK_OK:
+ break;
+
+ case DVDNAV_CELL_CHANGE:
+ {
+ result = dvdnav_current_title_program( dvdnav, &title, &pgcn, &pgn );
+ if (result != DVDNAV_STATUS_OK)
+ hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(dvdnav));
+
+ if ( title != cur_title && title > 0 )
+ {
+ hb_title_t * hbtitle;
+ int index;
+ index = find_title( list_title, title );
+ hbtitle = hb_list_item( list_title, index );
+ if ( hbtitle != NULL )
+ {
+ if ( hbtitle->duration > longest_duration )
+ {
+ longest_duration = hbtitle->duration;
+ longest = title;
+ }
+ }
+ // If the duration is longer than 10min, assume
+ // this isn't garbage leading to something bigger.
+ if ( longest_duration / 90000 > 10 * 60 )
+ {
+ return longest;
+ }
+ }
+ cur_title = title;
+ if (title > 0)
+ {
+ result = dvdnav_next_pg_search( dvdnav );
+ if ( result != DVDNAV_STATUS_OK )
+ {
+ return longest;
+ }
+ }
+ } break;
+
+ case DVDNAV_STILL_FRAME:
+ dvdnav_still_skip( dvdnav );
+ break;
+
+ case DVDNAV_WAIT:
+ dvdnav_wait_skip( dvdnav );
+ break;
+
+ case DVDNAV_STOP:
+ return 0;
+
+ case DVDNAV_HOP_CHANNEL:
+ break;
+
+ case DVDNAV_NAV_PACKET:
+ {
+ } break;
+
+ case DVDNAV_VTS_CHANGE:
+ {
+ result = dvdnav_current_title_program( dvdnav, &title, &pgcn, &pgn );
+ if (result != DVDNAV_STATUS_OK)
+ hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(dvdnav));
+
+ if ( title != cur_title && title > 0 )
+ {
+ hb_title_t * hbtitle;
+ int index;
+ index = find_title( list_title, title );
+ hbtitle = hb_list_item( list_title, index );
+ if ( hbtitle != NULL )
+ {
+ if ( hbtitle->duration > longest_duration )
+ {
+ longest_duration = hbtitle->duration;
+ longest = title;
+ }
+ }
+ if ( longest_duration / 90000 > 10 * 60 )
+ {
+ return longest;
+ }
+ }
+ cur_title = title;
+ } break;
+
+ case DVDNAV_HIGHLIGHT:
+ break;
+
+ case DVDNAV_AUDIO_STREAM_CHANGE:
+ break;
+
+ case DVDNAV_SPU_STREAM_CHANGE:
+ break;
+
+ case DVDNAV_SPU_CLUT_CHANGE:
+ break;
+
+ case DVDNAV_NOP:
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (cur_title > 0)
+ {
+ // Some titles have long lead-ins. Try skipping it.
+ dvdnav_next_pg_search( dvdnav );
+ }
+ }
+ return longest;
+}
+
+static int try_menu(
+ hb_dvdnav_t * d,
+ hb_list_t * list_title,
+ DVDMenuID_t menu,
+ uint64_t fallback_duration )
+{
+ int result, event, len;
+ uint8_t buf[HB_DVD_READ_BUFFER_SIZE];
+ int ii, jj;
+ int32_t cur_title, title, pgcn, pgn;
+ uint64_t longest_duration = 0;
+ int longest = -1;
+
+
+ result = dvdnav_menu_call( d->dvdnav, menu );
+ if ( result != DVDNAV_STATUS_OK )
+ {
+ // Sometimes the "first play" item doesn't initialize the
+ // initial VTS. So do it here.
+ result = dvdnav_title_play( d->dvdnav, 1 );
+ result = dvdnav_menu_call( d->dvdnav, menu );
+ if ( result != DVDNAV_STATUS_OK )
+ {
+ hb_error("dvdnav: Can not set dvd menu, %s", dvdnav_err_to_string(d->dvdnav));
+ goto done;
+ }
+ }
+
+ result = dvdnav_current_title_program( d->dvdnav, &title, &pgcn, &pgn );
+ if (result != DVDNAV_STATUS_OK)
+ hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(d->dvdnav));
+
+ cur_title = title;
+
+ for (jj = 0; jj < 4; jj++)
+ {
+ for (ii = 0; ii < 4000; ii++)
+ {
+ result = dvdnav_get_next_block( d->dvdnav, buf, &event, &len );
+ if ( result == DVDNAV_STATUS_ERR )
+ {
+ hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav));
+ goto done;
+ }
+ switch ( event )
+ {
+ case DVDNAV_BLOCK_OK:
+ break;
+
+ case DVDNAV_CELL_CHANGE:
+ {
+ result = dvdnav_current_title_program( d->dvdnav, &title, &pgcn, &pgn );
+ if (result != DVDNAV_STATUS_OK)
+ hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(d->dvdnav));
+ cur_title = title;
+ } break;
+
+ case DVDNAV_STILL_FRAME:
+ dvdnav_still_skip( d->dvdnav );
+ break;
+
+ case DVDNAV_WAIT:
+ dvdnav_wait_skip( d->dvdnav );
+ break;
+
+ case DVDNAV_STOP:
+ goto done;
+
+ case DVDNAV_HOP_CHANNEL:
+ break;
+
+ case DVDNAV_NAV_PACKET:
+ {
+ pci_t *pci = dvdnav_get_current_nav_pci( d->dvdnav );
+ int kk;
+ int buttons;
+ if ( pci == NULL ) break;
+
+ buttons = pci->hli.hl_gi.btn_ns;
+ if ( cur_title == 0 && buttons > 1 )
+ {
+ int menu_title, menu_id;
+ result = dvdnav_current_title_info( d->dvdnav, &menu_title, &menu_id );
+ if (result != DVDNAV_STATUS_OK)
+ hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(d->dvdnav));
+ for (kk = 0; kk < buttons; kk++)
+ {
+ dvdnav_t *dvdnav_copy;
+
+ result = dvdnav_dup( &dvdnav_copy, d->dvdnav );
+ if (result != DVDNAV_STATUS_OK)
+ {
+ hb_log("dvdnav dup failed: %s", dvdnav_err_to_string(d->dvdnav));
+ goto done;
+ }
+ title = try_button( dvdnav_copy, menu_id, kk, list_title );
+ dvdnav_free_dup( dvdnav_copy );
+
+ if ( title >= 0 )
+ {
+ hb_title_t * hbtitle;
+ int index;
+ index = find_title( list_title, title );
+ hbtitle = hb_list_item( list_title, index );
+ if ( hbtitle != NULL )
+ {
+ if ( hbtitle->duration > longest_duration )
+ {
+ longest_duration = hbtitle->duration;
+ longest = title;
+ if ((float)fallback_duration * 0.75 < longest_duration)
+ goto done;
+ }
+ }
+ }
+ }
+ goto done;
+ }
+ if ( cur_title == 0 && buttons == 1 )
+ {
+ dvdnav_button_select_and_activate( d->dvdnav, pci, 1 );
+ }
+ } break;
+
+ case DVDNAV_VTS_CHANGE:
+ {
+ result = dvdnav_current_title_program( d->dvdnav, &title, &pgcn, &pgn );
+ if (result != DVDNAV_STATUS_OK)
+ hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(d->dvdnav));
+ cur_title = title;
+ } break;
+
+ case DVDNAV_HIGHLIGHT:
+ break;
+
+ case DVDNAV_AUDIO_STREAM_CHANGE:
+ break;
+
+ case DVDNAV_SPU_STREAM_CHANGE:
+ break;
+
+ case DVDNAV_SPU_CLUT_CHANGE:
+ break;
+
+ case DVDNAV_NOP:
+ break;
+
+ default:
+ break;
+ }
+ }
+ // Sometimes the menu is preceeded by a intro that just
+ // gets restarted when hitting the menu button. So
+ // try skipping with the skip forward button. Then
+ // try hitting the menu again.
+ if ( !(jj & 1) )
+ dvdnav_next_pg_search( d->dvdnav );
+ else
+ result = dvdnav_menu_call( d->dvdnav, menu );
+ }
+
+done:
+ return longest;
+}
+
+static int hb_dvdnav_main_feature( hb_dvd_t * e, hb_list_t * list_title )
{
hb_dvdnav_t * d = &(e->dvdnav);
- ifo_handle_t *ifo;
+ int longest_root;
+ int longest_title;
+ int longest_fallback;
int ii;
- int vts, ttn, pgc_id, pgn;
- pgc_t *pgc;
- int cell_start, cell_end, title_start, title_end;
+ uint64_t longest_duration_root = 0;
+ uint64_t longest_duration_title = 0;
+ uint64_t longest_duration_fallback = 0;
- vts = d->vmg->tt_srpt->title[title-1].title_set_nr;
- ttn = d->vmg->tt_srpt->title[title-1].vts_ttn;
- if( !( ifo = ifoOpen( d->reader, vts ) ) )
+ for ( ii = 0; ii < hb_list_count( list_title ); ii++ )
{
- hb_error( "dvd: ifoOpen failed for VTS %d", vts );
- return 0;
+ hb_title_t * title = hb_list_item( list_title, ii );
+ if ( title->duration > longest_duration_fallback )
+ {
+ longest_duration_fallback = title->duration;
+ longest_fallback = title->index;
+ }
}
- /* Get title first and last blocks */
- pgc_id = ifo->vts_ptt_srpt->title[ttn-1].ptt[0].pgcn;
- pgn = ifo->vts_ptt_srpt->title[ttn-1].ptt[0].pgn;
- pgc = ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc;
- cell_start = pgc->program_map[pgn - 1] - 1;
- cell_end = pgc->nr_of_cells - 1;
- title_start = pgc->cell_playback[cell_start].first_sector;
- title_end = pgc->cell_playback[cell_end].last_sector;
-
- /* Block count */
- d->title_block_count = 0;
- for( ii = cell_start; ii <= cell_end; ii++ )
+ dvdnav_reset( d->dvdnav );
+ skip_some( d->dvdnav, 500 );
+
+ longest_root = try_menu( d, list_title, DVD_MENU_Root, longest_duration_fallback );
+ if ( longest_root >= 0 )
+ {
+ hb_title_t * hbtitle;
+ int index;
+ index = find_title( list_title, longest_root );
+ hbtitle = hb_list_item( list_title, index );
+ if ( hbtitle )
+ longest_duration_root = hbtitle->duration;
+ }
+ if ( longest_root < 0 ||
+ (float)longest_duration_fallback * 0.7 > longest_duration_root)
{
- d->title_block_count += pgc->cell_playback[ii].last_sector + 1 -
- pgc->cell_playback[ii].first_sector;
+ longest_title = try_menu( d, list_title, DVD_MENU_Title, longest_duration_fallback );
+ if ( longest_title >= 0 )
+ {
+ hb_title_t * hbtitle;
+ int index;
+ index = find_title( list_title, longest_title );
+ hbtitle = hb_list_item( list_title, index );
+ if ( hbtitle )
+ longest_duration_title = hbtitle->duration;
+ }
}
- ifoClose(ifo);
- if ( dvdnav_part_play(d->dvdnav, title, chapter) != DVDNAV_STATUS_OK )
+ uint64_t longest_duration;
+ int longest;
+
+ if ( longest_duration_root > longest_duration_title )
{
- hb_error( "dvd: dvdnav_title_play failed - %s",
+ longest_duration = longest_duration_root;
+ longest = longest_root;
+ }
+ else
+ {
+ longest_duration = longest_duration_title;
+ longest = longest_title;
+ }
+ if ((float)longest_duration_fallback * 0.7 > longest_duration)
+ {
+ longest = longest_fallback;
+ }
+ return longest;
+}
+
+/***********************************************************************
+ * hb_dvdnav_start
+ ***********************************************************************
+ * Title and chapter start at 1
+ **********************************************************************/
+static int hb_dvdnav_start( hb_dvd_t * e, hb_title_t *title, int c )
+{
+ hb_dvdnav_t * d = &(e->dvdnav);
+ int t = title->index;
+ hb_chapter_t *chapter;
+ dvdnav_status_t result;
+
+ d->title_block_count = title->block_count;
+ d->list_chapter = title->list_chapter;
+
+ if ( d->stopped && !hb_dvdnav_reset(d) )
+ {
+ return 0;
+ }
+ dvdnav_reset( d->dvdnav );
+ chapter = hb_list_item( title->list_chapter, c - 1);
+ if (chapter != NULL)
+ result = dvdnav_program_play(d->dvdnav, t, chapter->pgcn, chapter->pgn);
+ else
+ result = dvdnav_part_play(d->dvdnav, t, 1);
+ if (result != DVDNAV_STATUS_OK)
+ {
+ hb_error( "dvd: dvdnav_*_play failed - %s",
dvdnav_err_to_string(d->dvdnav) );
return 0;
}
- d->title = title;
+ d->title = t;
+ d->stopped = 0;
+ d->chapter = 0;
return 1;
}
static int hb_dvdnav_seek( hb_dvd_t * e, float f )
{
hb_dvdnav_t * d = &(e->dvdnav);
- uint64_t sector;
+ uint64_t sector = f * d->title_block_count;
int result, event, len;
uint8_t buf[HB_DVD_READ_BUFFER_SIZE];
int done = 0, ii;
+ if (d->stopped)
+ {
+ return 0;
+ }
+
+ // XXX the current version of libdvdnav can't seek outside the current
+ // PGC. Check if the place we're seeking to is in a different
+ // PGC. Position there & adjust the offset if so.
+ uint64_t pgc_offset = 0;
+ uint64_t chap_offset = 0;
+ hb_chapter_t *pgc_change = hb_list_item(d->list_chapter, 0 );
+ for ( ii = 0; ii < hb_list_count( d->list_chapter ); ++ii )
+ {
+ hb_chapter_t *chapter = hb_list_item( d->list_chapter, ii );
+ uint64_t chap_len = chapter->block_end - chapter->block_start + 1;
+
+ if ( chapter->pgcn != pgc_change->pgcn )
+ {
+ // this chapter's in a different pgc from the previous - note the
+ // change so we can make sector offset's be pgc relative.
+ pgc_offset = chap_offset;
+ pgc_change = chapter;
+ }
+ if ( chap_offset <= sector && sector < chap_offset + chap_len )
+ {
+ // this chapter contains the sector we want - see if it's in a
+ // different pgc than the one we're currently in.
+ int32_t title, pgcn, pgn;
+ if (dvdnav_current_title_program( d->dvdnav, &title, &pgcn, &pgn ) != DVDNAV_STATUS_OK)
+ hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(d->dvdnav));
+ // If we find ourselves in a new title, it means a title
+ // transition was made while reading data. Jumping between
+ // titles can cause the vm to get into a bad state. So
+ // reset the vm in this case.
+ if ( d->title != title )
+ dvdnav_reset( d->dvdnav );
+
+ if ( d->title != title || chapter->pgcn != pgcn )
+ {
+ // this chapter is in a different pgc - switch to it.
+ if (dvdnav_program_play(d->dvdnav, d->title, chapter->pgcn, chapter->pgn) != DVDNAV_STATUS_OK)
+ hb_log("dvdnav prog play err: %s", dvdnav_err_to_string(d->dvdnav));
+ }
+ // seek sectors are pgc-relative so remove the pgc start sector.
+ sector -= pgc_offset;
+ break;
+ }
+ chap_offset += chap_len;
+ }
+
// dvdnav will not let you seek or poll current position
// till it reaches a certain point in parsing. so we
// have to get blocks until we reach a cell
result = dvdnav_get_next_block( d->dvdnav, buf, &event, &len );
if ( result == DVDNAV_STATUS_ERR )
{
- hb_log("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav));
+ hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav));
return 0;
}
switch ( event )
case DVDNAV_BLOCK_OK:
case DVDNAV_CELL_CHANGE:
done = 1;
+ break;
case DVDNAV_STILL_FRAME:
dvdnav_still_skip( d->dvdnav );
break;
case DVDNAV_STOP:
+ hb_log("dvdnav: stop encountered during seek");
+ d->stopped = 1;
return 0;
case DVDNAV_HOP_CHANNEL:
default:
break;
}
- } while (!(event == DVDNAV_CELL_CHANGE || event == DVDNAV_BLOCK_OK));
+ }
- sector = f * d->title_block_count;
if (dvdnav_sector_search(d->dvdnav, sector, SEEK_SET) != DVDNAV_STATUS_OK)
{
hb_error( "dvd: dvdnav_sector_search failed - %s",
hb_dvdnav_t * d = &(e->dvdnav);
int result, event, len;
int chapter = 0;
+ int error_count = 0;
while ( 1 )
{
+ if (d->stopped)
+ {
+ return 0;
+ }
result = dvdnav_get_next_block( d->dvdnav, b->data, &event, &len );
if ( result == DVDNAV_STATUS_ERR )
{
- hb_log("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav));
- return 0;
+ hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav));
+ if (dvdnav_sector_search(d->dvdnav, 1, SEEK_CUR) != DVDNAV_STATUS_OK)
+ {
+ hb_error( "dvd: dvdnav_sector_search failed - %s",
+ dvdnav_err_to_string(d->dvdnav) );
+ return 0;
+ }
+ error_count++;
+ if (error_count > 10)
+ {
+ hb_error("dvdnav: Error, too many consecutive read errors");
+ return 0;
+ }
+ continue;
}
switch ( event )
{
if (chapter > 1)
b->new_chap = chapter;
chapter = 0;
+ error_count = 0;
return 1;
case DVDNAV_NOP:
* event can be used to query such information only when
* necessary and update the decoding/displaying accordingly.
*/
+ {
+ int tt = 0, pgcn = 0, pgn = 0;
+
+ dvdnav_current_title_program(d->dvdnav, &tt, &pgcn, &pgn);
+ if (tt != d->title)
+ {
+ // Transition to another title signals that we are done.
+ return 0;
+ }
+ }
break;
case DVDNAV_CELL_CHANGE:
* and update the decoding/displaying accordingly.
*/
{
- int tt = 0, ptt = 0;
+ int tt = 0, pgcn = 0, pgn = 0, c;
- dvdnav_current_title_info(d->dvdnav, &tt, &ptt);
+ dvdnav_current_title_program(d->dvdnav, &tt, &pgcn, &pgn);
if (tt != d->title)
{
// Transition to another title signals that we are done.
return 0;
}
- if (ptt > d->chapter)
- chapter = d->chapter = ptt;
+ c = FindChapterIndex(d->list_chapter, pgcn, pgn);
+ if (c != d->chapter)
+ {
+ if (c < d->chapter)
+ {
+ // Some titles end with a 'link' back to the beginning so
+ // a transition to an earlier chapter means we're done.
+ return 0;
+ }
+ chapter = d->chapter = c;
+ }
}
break;
/*
* Playback should end here.
*/
+ d->stopped = 1;
return 0;
default:
static int hb_dvdnav_chapter( hb_dvd_t * e )
{
hb_dvdnav_t * d = &(e->dvdnav);
- int32_t t, c;
+ int32_t t, pgcn, pgn;
+ int32_t c;
- if (dvdnav_current_title_info(d->dvdnav, &t, &c) != DVDNAV_STATUS_OK)
+ if (dvdnav_current_title_program(d->dvdnav, &t, &pgcn, &pgn) != DVDNAV_STATUS_OK)
{
return -1;
}
+ c = FindChapterIndex( d->list_chapter, pgcn, pgn );
return c;
}
}
/***********************************************************************
+ * FindChapterIndex
+ ***********************************************************************
+ * Assumes pgc and cell_cur are correctly set, and sets cell_next to the
+ * cell to be read when we will be done with cell_cur.
+ **********************************************************************/
+static int FindChapterIndex( hb_list_t * list, int pgcn, int pgn )
+{
+ int count, ii;
+ hb_chapter_t *chapter;
+
+ count = hb_list_count( list );
+ for (ii = 0; ii < count; ii++)
+ {
+ chapter = hb_list_item( list, ii );
+ if (chapter->pgcn == pgcn && chapter->pgn == pgn)
+ return chapter->index;
+ }
+ return 0;
+}
+
+/***********************************************************************
* FindNextCell
***********************************************************************
* Assumes pgc and cell_cur are correctly set, and sets cell_next to the
}
/***********************************************************************
+ * NextPgcn
+ ***********************************************************************
+ * Assumes pgc and cell_cur are correctly set, and sets cell_next to the
+ * cell to be read when we will be done with cell_cur.
+ * Since pg chains can be circularly linked (either from a read error or
+ * deliberately) pgcn_map tracks program chains we've already seen.
+ **********************************************************************/
+static int NextPgcn( ifo_handle_t *ifo, int pgcn, uint32_t pgcn_map[MAX_PGCN/32] )
+{
+ int next_pgcn;
+ pgc_t *pgc;
+
+ pgcn_map[pgcn >> 5] |= (1 << (pgcn & 31));
+
+ pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc;
+ next_pgcn = pgc->next_pgc_nr;
+ if ( next_pgcn < 1 || next_pgcn >= MAX_PGCN || next_pgcn > ifo->vts_pgcit->nr_of_pgci_srp )
+ return 0;
+
+ return pgcn_map[next_pgcn >> 5] & (1 << (next_pgcn & 31))? 0 : next_pgcn;
+}
+
+/***********************************************************************
+ * PgcWalkInit
+ ***********************************************************************
+ * Pgc links can loop. I track which have been visited in a bit vector
+ * Initialize the bit vector to empty.
+ **********************************************************************/
+static void PgcWalkInit( uint32_t pgcn_map[MAX_PGCN/32] )
+{
+ memset(pgcn_map, 0, sizeof(pgcn_map) );
+}
+
+/***********************************************************************
* dvdtime2msec
***********************************************************************
* From lsdvd