OSDN Git Service

Rate limit hb_error() messages to at most 1 message per second of the same message...
[handbrake-jp/handbrake-jp-git.git] / libhb / dvdnav.c
index ee73981..2f1ac93 100644 (file)
@@ -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, &region_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
@@ -887,7 +921,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 )
@@ -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 )
     {
@@ -952,9 +987,22 @@ static int hb_dvdnav_read( hb_dvd_t * e, hb_buffer_t * b )
         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;
         }
+        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) );
 }
 
 /***********************************************************************