X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fdvdnav.c;h=d525b9624b86a29a41330e2d231ad33967e5658d;hb=91808a1f62713db501fada2de3ba4fcabfff0b7a;hp=ee7398161b019d40111d8804d6aab896914e5d2a;hpb=67a2494fca9ce3c00649d967f15300a505143fe6;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/dvdnav.c b/libhb/dvdnav.c index ee739816..d525b962 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 @@ -43,12 +44,16 @@ hb_dvd_func_t hb_dvdnav_func = hb_dvdnav_set_angle }; +// 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( void ); +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 ); +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 * ); @@ -137,10 +142,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 ) { @@ -222,10 +238,11 @@ PttDuration(ifo_handle_t *ifo, int ttn, int pttn, int *blocks, int *last_pgcn) int i; // Initialize map of visited pgc's to prevent loops - PgcWalkInit(); + 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 ) + if ( pgcn < 1 || pgcn > ifo->vts_pgcit->nr_of_pgci_srp || pgcn >= MAX_PGCN) { hb_error( "invalid PGC ID %d, skipping", pgcn ); return 0; @@ -246,6 +263,12 @@ PttDuration(ifo_handle_t *ifo, int ttn, int pttn, int *blocks, int *last_pgcn) 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; @@ -257,7 +280,7 @@ PttDuration(ifo_handle_t *ifo, int ttn, int pttn, int *blocks, int *last_pgcn) } *last_pgcn = pgcn; pgn = 1; - } while((pgcn = NextPgcn(ifo, pgcn)) != 0); + } while((pgcn = NextPgcn(ifo, pgcn, pgcn_map)) != 0); return duration; } @@ -405,7 +428,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) /* Get pgc */ - if ( pgcn < 1 || pgcn > ifo->vts_pgcit->nr_of_pgci_srp ) + if ( pgcn < 1 || pgcn > ifo->vts_pgcit->nr_of_pgci_srp || pgcn >= MAX_PGCN) { hb_error( "invalid PGC ID %d for title %d, skipping", pgcn, t ); goto fail; @@ -413,7 +436,12 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; - hb_log("pgc_id: %d, pgn: %d: pgc: 0x%x", pgcn, pgn, pgc); + hb_log("pgc_id: %d, pgn: %d: pgc: %p", pgcn, pgn, pgc); + if (pgn > pgc->nr_of_programs) + { + hb_error( "invalid PGN %d for title %d, skipping", pgn, t ); + goto fail; + } /* Title start */ title->cell_start = pgc->program_map[pgn-1] - 1; @@ -435,7 +463,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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 ); @@ -560,12 +588,6 @@ 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, ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->palette, 16 * sizeof( uint32_t ) ); @@ -614,11 +636,15 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) 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; @@ -675,7 +701,8 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) } /* Chapters */ - PgcWalkInit(); + uint32_t pgcn_map[MAX_PGCN/32]; + PgcWalkInit( pgcn_map ); c = 0; do { @@ -693,7 +720,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) } pgn = 1; - } while ((pgcn = NextPgcn(ifo, pgcn)) != 0); + } while ((pgcn = NextPgcn(ifo, pgcn, pgcn_map)) != 0); hb_log( "scan: title %d has %d chapters", t, c ); @@ -701,11 +728,6 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) count = hb_list_count( title->list_chapter ); for (i = 0; i < count; i++) { - int pgcn_next, pgn_next; - pgc_t * pgc_next; - hb_chapter_t *chapter_next; - int pgc_cell_end; - chapter = hb_list_item( title->list_chapter, i ); pgcn = chapter->pgcn; @@ -714,41 +736,19 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t ) /* 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 < count - 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... */ - 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? */ - free( chapter ); - continue; - } + chapter->cell_end = pgc->nr_of_cells - 1; } else { - /* ... the last cell of the title */ - chapter->cell_end = pgc_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 */ chapter->block_count = 0; @@ -779,7 +779,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 ); @@ -848,6 +848,7 @@ static int hb_dvdnav_start( hb_dvd_t * e, hb_title_t *title, int c ) } d->title = t; d->stopped = 0; + d->chapter = 0; return 1; } @@ -868,7 +869,7 @@ 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; @@ -878,6 +879,39 @@ static int hb_dvdnav_seek( hb_dvd_t * e, float f ) 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. + 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 ); + + 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_change = chapter; + } + if ( chapter->block_start <= sector && sector <= chapter->block_end ) + { + // 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 ( 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_change->block_start; + break; + } + } + // 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 @@ -895,6 +929,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 ); @@ -920,9 +955,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", @@ -942,6 +976,7 @@ 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 ) { @@ -953,8 +988,21 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) if ( result == DVDNAV_STATUS_ERR ) { hb_log("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav)); - return 0; + 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_log("dvdnav: Error, too many consecutive read errors"); + return 0; + } + continue; } + error_count = 0; switch ( event ) { case DVDNAV_BLOCK_OK: @@ -1053,8 +1101,16 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b ) return 0; } c = FindChapterIndex(d->list_chapter, pgcn, pgn); - if (c > d->chapter) + 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; @@ -1225,36 +1281,27 @@ 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. + * 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 ) +static int NextPgcn( ifo_handle_t *ifo, int pgcn, uint32_t pgcn_map[MAX_PGCN/32] ) { - int byte; - uint8_t bit; int next_pgcn; pgc_t *pgc; - byte = pgcn >> 3; - bit = 1 << (pgcn & 0x7); - pgcn_map[byte] |= bit; + 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 > ifo->vts_pgcit->nr_of_pgci_srp ) + if ( next_pgcn < 1 || next_pgcn >= MAX_PGCN || 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; + return pgcn_map[next_pgcn >> 5] & (1 << (next_pgcn & 31))? 0 : next_pgcn; } /*********************************************************************** @@ -1263,9 +1310,9 @@ static int NextPgcn( ifo_handle_t *ifo, int pgcn ) * Pgc links can loop. I track which have been visited in a bit vector * Initialize the bit vector to empty. **********************************************************************/ -static void PgcWalkInit( void ) +static void PgcWalkInit( uint32_t pgcn_map[MAX_PGCN/32] ) { - memset(pgcn_map, 0, 128); + memset(pgcn_map, 0, sizeof(pgcn_map) ); } /***********************************************************************