X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fdvdnav.c;h=67e7b8b189fc3e695b7d96978ae1f0ae07215d74;hb=9972486f44c586225d98967441dcd3f3fd920636;hp=c46d7faae89dadade2d686274ec42ac9012a6527;hpb=64a48a58d954017539afc5e4f1d5c6201873149e;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/dvdnav.c b/libhb/dvdnav.c index c46d7faa..67e7b8b1 100644 --- a/libhb/dvdnav.c +++ b/libhb/dvdnav.c @@ -10,6 +10,7 @@ #include "dvdnav/dvdnav.h" #include "dvdread/ifo_read.h" +#include "dvdread/ifo_print.h" #include "dvdread/nav_read.h" #define DVD_READ_CACHE 1 @@ -18,14 +19,15 @@ static char * hb_dvdnav_name( char * path ); 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 = { @@ -40,15 +42,22 @@ 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 ) { @@ -79,6 +88,54 @@ static char * hb_dvdnav_name( char * path ) } /*********************************************************************** + * 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 *********************************************************************** * @@ -87,10 +144,21 @@ static hb_dvd_t * hb_dvdnav_init( char * path ) { 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 ) { @@ -162,6 +230,62 @@ static int hb_dvdnav_title_count( hb_dvd_t * e ) 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 **********************************************************************/ @@ -170,19 +294,22 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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 ) ); @@ -211,7 +338,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) } 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; @@ -219,31 +346,31 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) /* 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; } } @@ -255,55 +382,79 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) /* 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, @@ -311,25 +462,16 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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, @@ -341,11 +483,11 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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 ) ) { @@ -411,7 +553,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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, @@ -449,18 +591,8 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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; @@ -471,7 +603,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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 ) ) { @@ -479,9 +611,9 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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; @@ -498,18 +630,26 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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 ) { @@ -564,55 +704,56 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) } /* 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; @@ -625,18 +766,11 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) #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++ ) { @@ -648,7 +782,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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 ); @@ -656,7 +790,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) /* 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.; @@ -680,58 +814,537 @@ fail: 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; } @@ -752,11 +1365,61 @@ static void hb_dvdnav_stop( hb_dvd_t * e ) 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 @@ -766,7 +1429,7 @@ static int hb_dvdnav_seek( hb_dvd_t * e, float f ) 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 ) @@ -774,6 +1437,7 @@ static int hb_dvdnav_seek( hb_dvd_t * e, float f ) case DVDNAV_BLOCK_OK: case DVDNAV_CELL_CHANGE: done = 1; + break; case DVDNAV_STILL_FRAME: dvdnav_still_skip( d->dvdnav ); @@ -784,6 +1448,8 @@ static int hb_dvdnav_seek( hb_dvd_t * e, float f ) break; case DVDNAV_STOP: + hb_log("dvdnav: stop encountered during seek"); + d->stopped = 1; return 0; case DVDNAV_HOP_CHANNEL: @@ -797,9 +1463,8 @@ static int hb_dvdnav_seek( hb_dvd_t * e, float f ) 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", @@ -819,14 +1484,31 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) 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 ) { @@ -839,6 +1521,7 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) if (chapter > 1) b->new_chap = chapter; chapter = 0; + error_count = 0; return 1; case DVDNAV_NOP: @@ -907,6 +1590,16 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) * 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: @@ -917,16 +1610,25 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) * 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; @@ -964,6 +1666,7 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) /* * Playback should end here. */ + d->stopped = 1; return 0; default: @@ -982,12 +1685,14 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) 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; } @@ -1042,6 +1747,27 @@ static void hb_dvdnav_set_angle( hb_dvd_t * e, int angle ) } /*********************************************************************** + * 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 @@ -1074,6 +1800,40 @@ static int FindNextCell( pgc_t *pgc, int cell_cur ) } /*********************************************************************** + * 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