diff options
author | jstebbins <[email protected]> | 2009-05-01 17:27:51 +0000 |
---|---|---|
committer | jstebbins <[email protected]> | 2009-05-01 17:27:51 +0000 |
commit | 8cab795c817920ff023b6f0b8992a588e62c08c5 (patch) | |
tree | 37d6ba0f2bd800c152f23aba35edd7f42408b0c6 | |
parent | 0bef438147a6807abb2b594d30451bb5d97933aa (diff) |
dvdnav: improve title scanning
- looks for ptt with longest pgc sequence instead of just picking first ptt
solves many 'short title' problems
- constructs chapters from the pg's within the pgc's instead of using ptt's
solve the problem of ptt's that point to an intro pgc instead of
to the actual chapter position in the title.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@2361 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r-- | contrib/libdvdnav/A01-program-info.patch | 214 | ||||
-rw-r--r-- | libhb/common.h | 2 | ||||
-rw-r--r-- | libhb/dvd.c | 11 | ||||
-rw-r--r-- | libhb/dvd.h | 31 | ||||
-rw-r--r-- | libhb/dvdnav.c | 437 | ||||
-rw-r--r-- | libhb/internal.h | 2 | ||||
-rw-r--r-- | libhb/reader.c | 2 | ||||
-rw-r--r-- | libhb/scan.c | 2 |
8 files changed, 525 insertions, 176 deletions
diff --git a/contrib/libdvdnav/A01-program-info.patch b/contrib/libdvdnav/A01-program-info.patch new file mode 100644 index 000000000..531741b84 --- /dev/null +++ b/contrib/libdvdnav/A01-program-info.patch @@ -0,0 +1,214 @@ +diff -Naur libdvdnav.orig/src/dvdnav/dvdnav.h libdvdnav/src/dvdnav/dvdnav.h +--- libdvdnav.orig/src/dvdnav/dvdnav.h 2009-03-13 18:28:22.000000000 -0700 ++++ libdvdnav/src/dvdnav/dvdnav.h 2009-04-30 10:56:29.000000000 -0700 +@@ -279,6 +279,11 @@ + dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int32_t title, int32_t part); + + /* ++ * Plays the specified title, starting from the specified program ++ */ ++dvdnav_status_t dvdnav_program_play(dvdnav_t *this, int32_t title, int32_t pgcn, int32_t pgn); ++ ++/* + * Stores in *times an array (that the application *must* free) of + * dvdtimes corresponding to the chapter times for the chosen title. + * *duration will have the duration of the title +@@ -320,6 +325,13 @@ + int32_t *part); + + /* ++ * Return the title number, pgcn and pgn currently being played. ++ * A title of 0 indicates, we are in a menu. ++ */ ++dvdnav_status_t dvdnav_current_title_program(dvdnav_t *self, int32_t *title, ++ int32_t *pgcn, int32_t *pgn); ++ ++/* + * Return the current position (in blocks) within the current + * title and the length (in blocks) of said title. + * +diff -Naur libdvdnav.orig/src/navigation.c libdvdnav/src/navigation.c +--- libdvdnav.orig/src/navigation.c 2009-01-08 14:57:11.000000000 -0800 ++++ libdvdnav/src/navigation.c 2009-04-30 10:55:47.000000000 -0700 +@@ -122,10 +122,90 @@ + return DVDNAV_STATUS_ERR; + } + ++dvdnav_status_t dvdnav_current_title_program(dvdnav_t *this, int32_t *title, int32_t *pgcn, int32_t *pgn) { ++ int32_t retval; ++ int32_t part; ++ ++ pthread_mutex_lock(&this->vm_lock); ++ if (!this->vm->vtsi || !this->vm->vmgi) { ++ printerr("Bad VM state."); ++ pthread_mutex_unlock(&this->vm_lock); ++ return DVDNAV_STATUS_ERR; ++ } ++ if (!this->started) { ++ printerr("Virtual DVD machine not started."); ++ pthread_mutex_unlock(&this->vm_lock); ++ return DVDNAV_STATUS_ERR; ++ } ++ if (!this->vm->state.pgc) { ++ printerr("No current PGC."); ++ pthread_mutex_unlock(&this->vm_lock); ++ return DVDNAV_STATUS_ERR; ++ } ++ if ( (this->vm->state.domain == VTSM_DOMAIN) ++ || (this->vm->state.domain == VMGM_DOMAIN) ) { ++ /* Get current Menu ID: into *part. */ ++ if(! vm_get_current_menu(this->vm, &part)) { ++ pthread_mutex_unlock(&this->vm_lock); ++ return DVDNAV_STATUS_ERR; ++ } ++ if (part > -1) { ++ *title = 0; ++ *pgcn = this->vm->state.pgcN; ++ *pgn = this->vm->state.pgN; ++ pthread_mutex_unlock(&this->vm_lock); ++ return DVDNAV_STATUS_OK; ++ } ++ } ++ if (this->vm->state.domain == VTS_DOMAIN) { ++ retval = vm_get_current_title_part(this->vm, title, &part); ++ *pgcn = this->vm->state.pgcN; ++ *pgn = this->vm->state.pgN; ++ pthread_mutex_unlock(&this->vm_lock); ++ return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR; ++ } ++ printerr("Not in a title or menu."); ++ pthread_mutex_unlock(&this->vm_lock); ++ return DVDNAV_STATUS_ERR; ++} ++ + dvdnav_status_t dvdnav_title_play(dvdnav_t *this, int32_t title) { + return dvdnav_part_play(this, title, 1); + } + ++dvdnav_status_t dvdnav_program_play(dvdnav_t *this, int32_t title, int32_t pgcn, int32_t pgn) { ++ int32_t retval; ++ ++ pthread_mutex_lock(&this->vm_lock); ++ if (!this->vm->vmgi) { ++ printerr("Bad VM state."); ++ pthread_mutex_unlock(&this->vm_lock); ++ return DVDNAV_STATUS_ERR; ++ } ++ if (!this->started) { ++ /* don't report an error but be nice */ ++ vm_start(this->vm); ++ this->started = 1; ++ } ++ if (!this->vm->state.pgc) { ++ printerr("No current PGC."); ++ pthread_mutex_unlock(&this->vm_lock); ++ return DVDNAV_STATUS_ERR; ++ } ++ if((title < 1) || (title > this->vm->vmgi->tt_srpt->nr_of_srpts)) { ++ printerr("Title out of range."); ++ pthread_mutex_unlock(&this->vm_lock); ++ return DVDNAV_STATUS_ERR; ++ } ++ ++ retval = vm_jump_title_program(this->vm, title, pgcn, pgn); ++ if (retval) ++ this->vm->hop_channel++; ++ pthread_mutex_unlock(&this->vm_lock); ++ ++ return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR; ++} ++ + dvdnav_status_t dvdnav_part_play(dvdnav_t *this, int32_t title, int32_t part) { + int32_t retval; + +diff -Naur libdvdnav.orig/src/vm/vm.c libdvdnav/src/vm/vm.c +--- libdvdnav.orig/src/vm/vm.c 2009-03-13 18:28:22.000000000 -0700 ++++ libdvdnav/src/vm/vm.c 2009-04-30 11:07:35.000000000 -0700 +@@ -83,6 +83,8 @@ + static int set_PTT(vm_t *vm, int tt, int ptt); + static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn); + static int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part); ++static int set_PROG(vm_t *vm, int tt, int pgcn, int pgn); ++static int set_VTS_PROG(vm_t *vm, int vtsN, int vts_ttn, int pgcn, int pgn); + static int set_FP_PGC(vm_t *vm); + static int set_MENU(vm_t *vm, int menu); + static int set_PGCN(vm_t *vm, int pgcN); +@@ -516,6 +518,24 @@ + return 1; + } + ++int vm_jump_title_program(vm_t *vm, int title, int pgcn, int pgn) { ++ link_t link; ++ ++ if(!set_PROG(vm, title, pgcn, pgn)) ++ return 0; ++ /* Some DVDs do not want us to jump directly into a title and have ++ * PGC pre commands taking us back to some menu. Since we do not like that, ++ * we do not execute PGC pre commands that would do a jump. */ ++ /* process_command(vm, play_PGC_PG(vm, (vm->state).pgN)); */ ++ link = play_PGC_PG(vm, (vm->state).pgN); ++ if (link.command != PlayThis) ++ /* jump occured -> ignore it and play the PG anyway */ ++ process_command(vm, play_PG(vm)); ++ else ++ process_command(vm, link); ++ return 1; ++} ++ + int vm_jump_title_part(vm_t *vm, int title, int part) { + link_t link; + +@@ -1644,6 +1664,42 @@ + return res; + } + ++static int set_PROG(vm_t *vm, int tt, int pgcn, int pgn) { ++ assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts); ++ return set_VTS_PROG(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr, ++ vm->vmgi->tt_srpt->title[tt - 1].vts_ttn, pgcn, pgn); ++} ++ ++static int set_VTS_PROG(vm_t *vm, int vtsN, int vts_ttn, int pgcn, int pgn) { ++ int pgcN, pgN, res, title, part; ++ ++ (vm->state).domain = VTS_DOMAIN; ++ ++ if (vtsN != (vm->state).vtsN) ++ if (!ifoOpenNewVTSI(vm, vm->dvd, vtsN)) /* Also sets (vm->state).vtsN */ ++ return 0; ++ ++ if ((vts_ttn < 1) || (vts_ttn > vm->vtsi->vts_ptt_srpt->nr_of_srpts)) { ++ return 0; ++ } ++ ++ pgcN = pgcn; ++ pgN = pgn; ++ ++ (vm->state).TT_PGCN_REG = pgcN; ++ (vm->state).TTN_REG = get_TT(vm, vtsN, vts_ttn); ++ assert( (vm->state.TTN_REG) != 0 ); ++ (vm->state).VTS_TTN_REG = vts_ttn; ++ (vm->state).vtsN = vtsN; /* Not sure about this one. We can get to it easily from TTN_REG */ ++ /* Any other registers? */ ++ ++ res = set_PGCN(vm, pgcN); /* This clobber's state.pgN (sets it to 1), but we don't want clobbering here. */ ++ (vm->state).pgN = pgN; ++ vm_get_current_title_part(vm, &title, &part); ++ (vm->state).PTTN_REG = part; ++ return res; ++} ++ + static int set_FP_PGC(vm_t *vm) { + (vm->state).domain = FP_DOMAIN; + if (!vm->vmgi->first_play_pgc) { +diff -Naur libdvdnav.orig/src/vm/vm.h libdvdnav/src/vm/vm.h +--- libdvdnav.orig/src/vm/vm.h 2009-03-13 18:28:22.000000000 -0700 ++++ libdvdnav/src/vm/vm.h 2009-04-30 10:57:02.000000000 -0700 +@@ -139,6 +139,7 @@ + int vm_jump_pg(vm_t *vm, int pg); + int vm_jump_cell_block(vm_t *vm, int cell, int block); + int vm_jump_title_part(vm_t *vm, int title, int part); ++int vm_jump_title_program(vm_t *vm, int title, int pgcn, int pgn); + int vm_jump_top_pg(vm_t *vm); + int vm_jump_next_pg(vm_t *vm); + int vm_jump_prev_pg(vm_t *vm); diff --git a/libhb/common.h b/libhb/common.h index 73ebfda76..1c58286fe 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -422,6 +422,8 @@ struct hb_audio_s struct hb_chapter_s { int index; + int pgcn; + int pgn; int cell_start; int cell_end; int block_start; diff --git a/libhb/dvd.c b/libhb/dvd.c index 8bc61944f..cf6eefe49 100644 --- a/libhb/dvd.c +++ b/libhb/dvd.c @@ -16,7 +16,7 @@ static void hb_dvdread_close( hb_dvd_t ** _d ); static char * hb_dvdread_name( char * path ); static int hb_dvdread_title_count( hb_dvd_t * d ); static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * d, int t ); -static int hb_dvdread_start( hb_dvd_t * d, int title, int chapter ); +static int hb_dvdread_start( hb_dvd_t * d, hb_title_t *title, int chapter ); static void hb_dvdread_stop( hb_dvd_t * d ); static int hb_dvdread_seek( hb_dvd_t * d, float f ); static int hb_dvdread_read( hb_dvd_t * d, hb_buffer_t * b ); @@ -651,15 +651,16 @@ cleanup: *********************************************************************** * Title and chapter start at 1 **********************************************************************/ -static int hb_dvdread_start( hb_dvd_t * e, int title, int chapter ) +static int hb_dvdread_start( hb_dvd_t * e, hb_title_t *title, int chapter ) { hb_dvdread_t *d = &(e->dvdread); int pgc_id, pgn; int i; + int t = title->index; /* Open the IFO and the VOBs for this title */ - d->vts = d->vmg->tt_srpt->title[title-1].title_set_nr; - d->ttn = d->vmg->tt_srpt->title[title-1].vts_ttn; + d->vts = d->vmg->tt_srpt->title[t-1].title_set_nr; + d->ttn = d->vmg->tt_srpt->title[t-1].vts_ttn; if( !( d->ifo = ifoOpen( d->reader, d->vts ) ) ) { hb_error( "dvd: ifoOpen failed for VTS %d", d->vts ); @@ -1257,7 +1258,7 @@ hb_title_t * hb_dvd_title_scan( hb_dvd_t * d, int t ) return dvd_methods->title_scan(d, t); } -int hb_dvd_start( hb_dvd_t * d, int title, int chapter ) +int hb_dvd_start( hb_dvd_t * d, hb_title_t *title, int chapter ) { return dvd_methods->start(d, title, chapter); } diff --git a/libhb/dvd.h b/libhb/dvd.h index 811efe5b6..bcba78ea4 100644 --- a/libhb/dvd.h +++ b/libhb/dvd.h @@ -49,8 +49,9 @@ struct hb_dvdnav_s dvd_reader_t * reader; ifo_handle_t * vmg; int title; - int title_block_count; + int title_block_count; int chapter; + hb_list_t * list_chapter; int stopped; }; @@ -59,25 +60,25 @@ typedef struct hb_dvdread_s hb_dvdread_t; union hb_dvd_s { - hb_dvdread_t dvdread; - hb_dvdnav_t dvdnav; + hb_dvdread_t dvdread; + hb_dvdnav_t dvdnav; }; struct hb_dvd_func_s { - hb_dvd_t * (* init) ( char * ); - void (* close) ( hb_dvd_t ** ); - char * (* name) ( char * ); - int (* title_count) ( hb_dvd_t * ); - hb_title_t * (* title_scan) ( hb_dvd_t *, int ); - int (* start) ( hb_dvd_t *, int, int ); - void (* stop) ( hb_dvd_t * ); - int (* seek) ( hb_dvd_t *, float ); - int (* read) ( hb_dvd_t *, hb_buffer_t * ); - int (* chapter) ( hb_dvd_t * ); - int (* angle_count) ( hb_dvd_t * ); - void (* set_angle) ( hb_dvd_t *, int ); + hb_dvd_t * (* init) ( char * ); + void (* close) ( hb_dvd_t ** ); + char * (* name) ( char * ); + int (* title_count) ( hb_dvd_t * ); + hb_title_t * (* title_scan) ( hb_dvd_t *, int ); + int (* start) ( hb_dvd_t *, hb_title_t *, int ); + void (* stop) ( hb_dvd_t * ); + int (* seek) ( hb_dvd_t *, float ); + int (* read) ( hb_dvd_t *, hb_buffer_t * ); + int (* chapter) ( hb_dvd_t * ); + int (* angle_count) ( hb_dvd_t * ); + void (* set_angle) ( hb_dvd_t *, int ); }; typedef struct hb_dvd_func_s hb_dvd_func_t; diff --git a/libhb/dvdnav.c b/libhb/dvdnav.c index eee9d28b9..ee7398161 100644 --- a/libhb/dvdnav.c +++ b/libhb/dvdnav.c @@ -18,7 +18,7 @@ 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 ); @@ -46,9 +46,11 @@ hb_dvd_func_t hb_dvdnav_func = /*********************************************************************** * Local prototypes **********************************************************************/ +static void PgcWalkInit( void ); +static int FindChapterIndex( hb_list_t * list, int pgcn, int pgn ); +static int NextPgcn( ifo_handle_t *ifo, int pgcn ); 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 ) { @@ -88,7 +90,7 @@ static char * hb_dvdnav_name( char * path ) static int hb_dvdnav_reset( hb_dvdnav_t * d ) { if ( d->dvdnav ) - dvdnav_close( d->dvdnav ); + dvdnav_close( d->dvdnav ); /* Open device */ if( dvdnav_open(&d->dvdnav, d->path) != DVDNAV_STATUS_OK ) @@ -119,7 +121,7 @@ static int hb_dvdnav_reset( hb_dvdnav_t * d ) dvdnav_err_to_string(d->dvdnav)); goto fail; } - return 1; + return 1; fail: if( d->dvdnav ) dvdnav_close( d->dvdnav ); @@ -210,6 +212,55 @@ 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 + PgcWalkInit(); + 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 ) + { + 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; + } + 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)) != 0); + return duration; +} + /*********************************************************************** * hb_dvdnav_title_scan **********************************************************************/ @@ -218,13 +269,15 @@ 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; @@ -259,7 +312,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; @@ -267,31 +320,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; } } @@ -303,55 +356,74 @@ 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; } - - /* 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 ) + 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++ ) { - hb_error( "invalid PGC ID %d for title %d, skipping", pgc_id, t ); - goto fail; - } - pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgn; - pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc; + int blocks = 0; - hb_log("pgc_id: %d, pgn: %d: pgc: 0x%x", pgc_id, pgn, pgc); + 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; + } + } - if( !pgc ) + /* 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( "scan: pgc not valid, skipping" ); + hb_log( "scan: ignoring title (too short)" ); goto fail; } - if( pgn <= 0 || pgn > 99 ) + pgcn = longest_pgcn; + pgcn_end = longest_pgcn_end; + pgn = longest_pgn;; + title_pgcn = pgcn; + + + /* Get pgc */ + if ( pgcn < 1 || pgcn > ifo->vts_pgcit->nr_of_pgci_srp ) { - hb_error( "scan: pgn %d not valid, skipping", pgn ); + hb_error( "invalid PGC ID %d for title %d, skipping", pgcn, t ); goto fail; } - /* Start cell */ - title->cell_start = pgc->program_map[pgn-1] - 1; + pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; + + hb_log("pgc_id: %d, pgn: %d: pgc: 0x%x", pgcn, pgn, pgc); + + /* 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, @@ -359,7 +431,7 @@ 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; @@ -367,17 +439,8 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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, @@ -389,11 +452,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 ) ) { @@ -459,7 +522,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, @@ -504,11 +567,11 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) } memcpy( title->palette, - vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->palette, + ifo->vts_pgcit->pgci_srp[title_pgcn-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; @@ -519,7 +582,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 ) ) { @@ -527,9 +590,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; @@ -546,9 +609,9 @@ 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->id = ( ( 0x20 + position ) << 8 ) | 0xbd; @@ -612,36 +675,66 @@ 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++ ) + PgcWalkInit(); + c = 0; + do + { + pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; + + 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)) != 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++) { - int pgc_id_next, pgn_next; + int pgcn_next, pgn_next; pgc_t * pgc_next; + hb_chapter_t *chapter_next; + int pgc_cell_end; - chapter = calloc( sizeof( hb_chapter_t ), 1 ); - /* remember the on-disc chapter number */ - chapter->index = i + 1; + 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; + pgc_cell_end = pgc->nr_of_cells - 1; /* End cell */ - if( i != vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts - 1 ) + if( i < count - 1 ) { /* 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; + chapter_next = hb_list_item( title->list_chapter, i+1 ); + pgcn_next = chapter_next->pgcn; + pgn_next = chapter_next->pgn; + pgc_next = ifo->vts_pgcit->pgci_srp[pgcn_next-1].pgc; + if (pgcn_next == pgcn) + { + chapter->cell_end = pgc_next->program_map[pgn_next-1] - 2; + } + else + { + chapter->cell_end = pgc_cell_end; + } if( chapter->cell_end < 0 ) { /* Huh? */ @@ -652,15 +745,12 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) else { /* ... the last cell of the title */ - chapter->cell_end = title->cell_end; + chapter->cell_end = pgc_cell_end; } 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; @@ -673,18 +763,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++ ) { @@ -704,7 +787,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.; @@ -728,7 +811,7 @@ fail: title = NULL; cleanup: - if( vts ) ifoClose( vts ); + if( ifo ) ifoClose( ifo ); return title; } @@ -738,53 +821,33 @@ cleanup: *********************************************************************** * Title and chapter start at 1 **********************************************************************/ -static int hb_dvdnav_start( hb_dvd_t * e, int title, int chapter ) +static int hb_dvdnav_start( hb_dvd_t * e, hb_title_t *title, int c ) { hb_dvdnav_t * d = &(e->dvdnav); - ifo_handle_t *ifo; - int ii; - int vts, ttn, pgc_id, pgn; - pgc_t *pgc; - int cell_start, cell_end, title_start, title_end; - - 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 ) ) ) - { - hb_error( "dvd: ifoOpen failed for VTS %d", vts ); - return 0; - } + int t = title->index; + hb_chapter_t *chapter; + dvdnav_status_t result; - /* 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++ ) - { - d->title_block_count += pgc->cell_playback[ii].last_sector + 1 - - pgc->cell_playback[ii].first_sector; - } - ifoClose(ifo); + d->title_block_count = title->block_count; + d->list_chapter = title->list_chapter; - if ( d->stopped && !hb_dvdnav_reset(d) ) + if ( d->stopped && !hb_dvdnav_reset(d) ) { return 0; } - if ( dvdnav_part_play(d->dvdnav, title, chapter) != DVDNAV_STATUS_OK ) + 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_title_play failed - %s", + hb_error( "dvd: dvdnav_*_play failed - %s", dvdnav_err_to_string(d->dvdnav) ); return 0; } - d->title = title; - d->stopped = 0; + d->title = t; + d->stopped = 0; return 1; } @@ -795,7 +858,6 @@ static int hb_dvdnav_start( hb_dvd_t * e, int title, int chapter ) **********************************************************************/ static void hb_dvdnav_stop( hb_dvd_t * e ) { - hb_dvdnav_t * d = &(e->dvdnav); } /*********************************************************************** @@ -812,9 +874,10 @@ static int hb_dvdnav_seek( hb_dvd_t * e, float f ) int done = 0, ii; if (d->stopped) - { - return 0; - } + { + return 0; + } + // 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 @@ -842,7 +905,8 @@ static int hb_dvdnav_seek( hb_dvd_t * e, float f ) break; case DVDNAV_STOP: - d->stopped = 1; + hb_log("dvdnav: stop encountered during seek"); + d->stopped = 1; return 0; case DVDNAV_HOP_CHANNEL: @@ -881,10 +945,10 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) while ( 1 ) { - if (d->stopped) - { - return 0; - } + if (d->stopped) + { + return 0; + } result = dvdnav_get_next_block( d->dvdnav, b->data, &event, &len ); if ( result == DVDNAV_STATUS_ERR ) { @@ -980,16 +1044,17 @@ 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) + chapter = d->chapter = c; } break; @@ -1027,7 +1092,7 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) /* * Playback should end here. */ - d->stopped = 1; + d->stopped = 1; return 0; default: @@ -1046,12 +1111,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; } @@ -1106,6 +1173,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 @@ -1137,6 +1225,49 @@ static int FindNextCell( pgc_t *pgc, int cell_cur ) return cell_next; } +static uint8_t pgcn_map[1280]; + +/*********************************************************************** + * 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. + **********************************************************************/ +static int NextPgcn( ifo_handle_t *ifo, int pgcn ) +{ + int byte; + uint8_t bit; + int next_pgcn; + pgc_t *pgc; + + byte = pgcn >> 3; + bit = 1 << (pgcn & 0x7); + pgcn_map[byte] |= bit; + + pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; + next_pgcn = pgc->next_pgc_nr; + if ( next_pgcn < 1 || next_pgcn > ifo->vts_pgcit->nr_of_pgci_srp ) + return 0; + + byte = next_pgcn >> 3; + bit = 1 << (pgcn & 0x7); + if (pgcn_map[byte] & bit) + return 0; + + return 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( void ) +{ + memset(pgcn_map, 0, 128); +} + /*********************************************************************** * dvdtime2msec *********************************************************************** diff --git a/libhb/internal.h b/libhb/internal.h index 8d927501c..7d7594802 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -174,7 +174,7 @@ typedef struct hb_stream_s hb_stream_t; hb_dvd_t * hb_dvd_init( char * path ); int hb_dvd_title_count( hb_dvd_t * ); hb_title_t * hb_dvd_title_scan( hb_dvd_t *, int title ); -int hb_dvd_start( hb_dvd_t *, int title, int chapter ); +int hb_dvd_start( hb_dvd_t *, hb_title_t *title, int chapter ); void hb_dvd_stop( hb_dvd_t * ); int hb_dvd_seek( hb_dvd_t *, float ); int hb_dvd_read( hb_dvd_t *, hb_buffer_t * ); diff --git a/libhb/reader.c b/libhb/reader.c index 78f24a457..0acbe0921 100644 --- a/libhb/reader.c +++ b/libhb/reader.c @@ -220,7 +220,7 @@ static void ReaderFunc( void * _r ) } /* end chapter mapping XXX */ - if( !hb_dvd_start( r->dvd, r->title->index, start ) ) + if( !hb_dvd_start( r->dvd, r->title, start ) ) { hb_dvd_close( &r->dvd ); return; diff --git a/libhb/scan.c b/libhb/scan.c index db179ef75..a549285b5 100644 --- a/libhb/scan.c +++ b/libhb/scan.c @@ -407,7 +407,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) if (data->dvd) { - hb_dvd_start( data->dvd, title->index, 1 ); + hb_dvd_start( data->dvd, title, 1 ); title->angle_count = hb_dvd_angle_count( data->dvd ); hb_log( "scan: title angle(s) %d", title->angle_count ); } |