1 /* dvdnav.c
2 
3    Copyright (c) 2003-2021 HandBrake Team
4    This file is part of the HandBrake source code
5    Homepage: <http://handbrake.fr/>.
6    It may be used under the terms of the GNU General Public License v2.
7    For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
8  */
9 
10 #include "libavcodec/avcodec.h"
11 
12 #include "handbrake/handbrake.h"
13 #include "handbrake/lang.h"
14 #include "handbrake/dvd.h"
15 
16 #include "dvdnav/dvdnav.h"
17 #include "dvdread/ifo_read.h"
18 #include "dvdread/ifo_print.h"
19 #include "dvdread/nav_read.h"
20 
21 #define DVD_READ_CACHE 1
22 
23 static char        * hb_dvdnav_name( char * path );
24 static hb_dvd_t    * hb_dvdnav_init( hb_handle_t * h, const char * path );
25 static int           hb_dvdnav_title_count( hb_dvd_t * d );
26 static hb_title_t  * hb_dvdnav_title_scan( hb_dvd_t * d, int t, uint64_t min_duration );
27 static int           hb_dvdnav_start( hb_dvd_t * d, hb_title_t *title, int chapter );
28 static void          hb_dvdnav_stop( hb_dvd_t * d );
29 static int           hb_dvdnav_seek( hb_dvd_t * d, float f );
30 static hb_buffer_t * hb_dvdnav_read( hb_dvd_t * d );
31 static int           hb_dvdnav_chapter( hb_dvd_t * d );
32 static void          hb_dvdnav_close( hb_dvd_t ** _d );
33 static int           hb_dvdnav_angle_count( hb_dvd_t * d );
34 static void          hb_dvdnav_set_angle( hb_dvd_t * d, int angle );
35 static int           hb_dvdnav_main_feature( hb_dvd_t * d, hb_list_t * list_title );
36 
37 hb_dvd_func_t hb_dvdnav_func =
38 {
39     hb_dvdnav_init,
40     hb_dvdnav_close,
41     hb_dvdnav_name,
42     hb_dvdnav_title_count,
43     hb_dvdnav_title_scan,
44     hb_dvdnav_start,
45     hb_dvdnav_stop,
46     hb_dvdnav_seek,
47     hb_dvdnav_read,
48     hb_dvdnav_chapter,
49     hb_dvdnav_angle_count,
50     hb_dvdnav_set_angle,
51     hb_dvdnav_main_feature
52 };
53 
54 // there can be at most 999 PGCs per title. round that up to the nearest
55 // power of two.
56 #define MAX_PGCN 1024
57 
58 /***********************************************************************
59  * Local prototypes
60  **********************************************************************/
61 static void PgcWalkInit( uint32_t pgcn_map[MAX_PGCN/32] );
62 static int FindChapterIndex( hb_list_t * list, int pgcn, int pgn );
63 static int NextPgcn( ifo_handle_t *ifo, int pgcn, uint32_t pgcn_map[MAX_PGCN/32] );
64 static int FindNextCell( pgc_t *pgc, int cell_cur );
65 static int dvdtime2msec( dvd_time_t * );
66 static int TitleOpenIfo(hb_dvdnav_t * d, int t);
67 static void TitleCloseIfo(hb_dvdnav_t * d);
68 
hb_dvdnav_methods(void)69 hb_dvd_func_t * hb_dvdnav_methods( void )
70 {
71     return &hb_dvdnav_func;
72 }
73 
hb_dvdnav_name(char * path)74 static char * hb_dvdnav_name( char * path )
75 {
76     static char name[1024];
77     unsigned char unused[1024];
78     dvd_reader_t * reader;
79 
80     reader = DVDOpen( path );
81     if( !reader )
82     {
83         return NULL;
84     }
85 
86     if( DVDUDFVolumeInfo( reader, name, sizeof( name ),
87                           unused, sizeof( unused ) ) )
88     {
89         DVDClose( reader );
90         return NULL;
91     }
92 
93     DVDClose( reader );
94     return name;
95 }
96 
97 /***********************************************************************
98  * hb_dvdnav_reset
99  ***********************************************************************
100  * Once dvdnav has entered the 'stopped' state, it can not be revived
101  * dvdnav_reset doesn't work because it doesn't remember the path
102  * So this function re-opens dvdnav
103  **********************************************************************/
hb_dvdnav_reset(hb_dvdnav_t * d)104 static int hb_dvdnav_reset( hb_dvdnav_t * d )
105 {
106     char * path_ccp = hb_utf8_to_cp( d->path );
107     if ( d->dvdnav )
108         dvdnav_close( d->dvdnav );
109 
110     /* Open device */
111     if( dvdnav_open(&d->dvdnav, path_ccp) != DVDNAV_STATUS_OK )
112     {
113         /*
114          * Not an error, may be a stream - which we'll try in a moment.
115          */
116         hb_log( "dvd: not a dvd - trying as a stream/file instead" );
117         goto fail;
118     }
119 
120     if (dvdnav_set_readahead_flag(d->dvdnav, DVD_READ_CACHE) !=
121         DVDNAV_STATUS_OK)
122     {
123         hb_error("Error: dvdnav_set_readahead_flag: %s\n",
124                  dvdnav_err_to_string(d->dvdnav));
125         goto fail;
126     }
127 
128     /*
129      ** set the PGC positioning flag to have position information
130      ** relatively to the whole feature instead of just relatively to the
131      ** current chapter
132      **/
133     if (dvdnav_set_PGC_positioning_flag(d->dvdnav, 1) != DVDNAV_STATUS_OK)
134     {
135         hb_error("Error: dvdnav_set_PGC_positioning_flag: %s\n",
136                  dvdnav_err_to_string(d->dvdnav));
137         goto fail;
138     }
139 
140     free( path_ccp );
141 
142     return 1;
143 
144 fail:
145     if( d->dvdnav ) dvdnav_close( d->dvdnav );
146     free( path_ccp );
147     return 0;
148 }
149 
150 /***********************************************************************
151  * hb_dvdnav_init
152  ***********************************************************************
153  *
154  **********************************************************************/
hb_dvdnav_init(hb_handle_t * h,const char * path)155 static hb_dvd_t * hb_dvdnav_init( hb_handle_t * h, const char * path )
156 {
157     hb_dvd_t * e;
158     hb_dvdnav_t * d;
159     int region_mask;
160     char * path_ccp;
161 
162     e = calloc( sizeof( hb_dvd_t ), 1 );
163     d = &(e->dvdnav);
164     d->h = h;
165 
166     /*
167      * Convert UTF-8 path to current code page on Windows
168      * hb_utf8_to_cp() is the same as strdup on non-Windows,
169      * so no #ifdef required here
170      */
171     path_ccp = hb_utf8_to_cp( path );
172 
173 	/* Log DVD drive region code */
174     if ( hb_dvd_region( path_ccp, &region_mask ) == 0 )
175     {
176         hb_log( "dvd: Region mask 0x%02x", region_mask );
177         if ( region_mask == 0xFF )
178         {
179             hb_log( "dvd: Warning, DVD device has no region set" );
180         }
181     }
182 
183     /* Open device */
184     if( dvdnav_open(&d->dvdnav, path_ccp) != DVDNAV_STATUS_OK )
185     {
186         /*
187          * Not an error, may be a stream - which we'll try in a moment.
188          */
189         hb_log( "dvd: not a dvd - trying as a stream/file instead" );
190         goto fail;
191     }
192 
193     if (dvdnav_set_readahead_flag(d->dvdnav, DVD_READ_CACHE) !=
194         DVDNAV_STATUS_OK)
195     {
196         hb_error("Error: dvdnav_set_readahead_flag: %s\n",
197                  dvdnav_err_to_string(d->dvdnav));
198         goto fail;
199     }
200 
201     /*
202      ** set the PGC positioning flag to have position information
203      ** relatively to the whole feature instead of just relatively to the
204      ** current chapter
205      **/
206     if (dvdnav_set_PGC_positioning_flag(d->dvdnav, 1) != DVDNAV_STATUS_OK)
207     {
208         hb_error("Error: dvdnav_set_PGC_positioning_flag: %s\n",
209                  dvdnav_err_to_string(d->dvdnav));
210         goto fail;
211     }
212 
213     /* Open device */
214     if( !( d->reader = DVDOpen( path_ccp ) ) )
215     {
216         /*
217          * Not an error, may be a stream - which we'll try in a moment.
218          */
219         hb_log( "dvd: not a dvd - trying as a stream/file instead" );
220         goto fail;
221     }
222 
223     /* Open main IFO */
224     if( !( d->vmg = ifoOpen( d->reader, 0 ) ) )
225     {
226         hb_error( "dvd: ifoOpen failed" );
227         goto fail;
228     }
229 
230     d->path = strdup( path ); /* hb_dvdnav_title_scan assumes UTF-8 path, so not path_ccp here */
231     free( path_ccp );
232 
233     return e;
234 
235 fail:
236     if( d->dvdnav ) dvdnav_close( d->dvdnav );
237     if( d->vmg )    ifoClose( d->vmg );
238     if( d->reader ) DVDClose( d->reader );
239     free( e );
240     free( path_ccp );
241     return NULL;
242 }
243 
244 /***********************************************************************
245  * hb_dvdnav_title_count
246  **********************************************************************/
hb_dvdnav_title_count(hb_dvd_t * e)247 static int hb_dvdnav_title_count( hb_dvd_t * e )
248 {
249     int titles = 0;
250     hb_dvdnav_t * d = &(e->dvdnav);
251 
252     dvdnav_get_number_of_titles(d->dvdnav, &titles);
253     return titles;
254 }
255 
256 static uint64_t
PttDuration(ifo_handle_t * ifo,int ttn,int pttn,int * blocks,int * last_pgcn)257 PttDuration(ifo_handle_t *ifo, int ttn, int pttn, int *blocks, int *last_pgcn)
258 {
259     int            pgcn, pgn;
260     pgc_t        * pgc;
261     uint64_t       duration = 0;
262     int            cell_start, cell_end;
263     int            i;
264 
265     *blocks = 0;
266 
267     // Initialize map of visited pgc's to prevent loops
268     uint32_t pgcn_map[MAX_PGCN/32];
269     PgcWalkInit( pgcn_map );
270     pgcn   = ifo->vts_ptt_srpt->title[ttn-1].ptt[pttn-1].pgcn;
271     pgn   = ifo->vts_ptt_srpt->title[ttn-1].ptt[pttn-1].pgn;
272     if ( pgcn < 1 || pgcn > ifo->vts_pgcit->nr_of_pgci_srp || pgcn >= MAX_PGCN)
273     {
274         hb_log( "invalid PGC ID %d, skipping", pgcn );
275         return 0;
276     }
277 
278     if( pgn <= 0 || pgn > 99 )
279     {
280         hb_log( "scan: pgn %d not valid, skipping", pgn );
281         return 0;
282     }
283 
284     do
285     {
286         pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc;
287         if (!pgc)
288         {
289             *blocks = 0;
290             duration = 0;
291             hb_log( "scan: pgc not valid, skipping" );
292             break;
293         }
294 
295         if (pgc->cell_playback == NULL)
296         {
297             *blocks = 0;
298             duration = 0;
299             hb_log("invalid PGC cell_playback table, skipping");
300             break;
301         }
302 
303         if (pgn > pgc->nr_of_programs)
304         {
305             pgn = 1;
306             continue;
307         }
308 
309         duration += 90LL * dvdtime2msec( &pgc->playback_time );
310 
311         cell_start = pgc->program_map[pgn-1] - 1;
312         cell_end = pgc->nr_of_cells - 1;
313         for(i = cell_start; i <= cell_end; i = FindNextCell(pgc, i))
314         {
315             *blocks += pgc->cell_playback[i].last_sector + 1 -
316                 pgc->cell_playback[i].first_sector;
317         }
318         *last_pgcn = pgcn;
319         pgn = 1;
320     } while((pgcn = NextPgcn(ifo, pgcn, pgcn_map)) != 0);
321     return duration;
322 }
323 
add_subtitle(hb_list_t * list_subtitle,int position,iso639_lang_t * lang,int lang_extension,uint8_t * palette,int style)324 static void add_subtitle( hb_list_t * list_subtitle, int position,
325                           iso639_lang_t * lang, int lang_extension,
326                           uint8_t * palette, int style )
327 {
328     hb_subtitle_t * subtitle;
329     int ii, count;
330 
331     count = hb_list_count(list_subtitle);
332     for (ii = 0; ii < count; ii++)
333     {
334         subtitle = hb_list_item(list_subtitle, ii);
335         if (((subtitle->id >> 8) & 0x1f) == position)
336         {
337             // The subtitle is already in the list
338             return;
339         }
340     }
341 
342     subtitle        = calloc(sizeof(hb_subtitle_t), 1);
343     subtitle->track = count;
344     subtitle->id    = ((0x20 + position) << 8) | 0xbd;
345     snprintf(subtitle->lang, sizeof( subtitle->lang ), "%s",
346              strlen(lang->native_name) ? lang->native_name : lang->eng_name);
347     snprintf(subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s",
348              lang->iso639_2);
349     subtitle->format         = PICTURESUB;
350     subtitle->source         = VOBSUB;
351     subtitle->config.dest    = RENDERSUB;
352     subtitle->stream_type    = 0xbd;
353     subtitle->substream_type = 0x20 + position;
354     subtitle->codec          = WORK_DECAVSUB;
355     subtitle->codec_param    = AV_CODEC_ID_DVD_SUBTITLE;
356     subtitle->timebase.num   = 1;
357     subtitle->timebase.den   = 90000;
358 
359     memcpy(subtitle->palette, palette, 16 * sizeof(uint32_t));
360     subtitle->palette_set = 1;
361 
362     const char * name = NULL;
363     switch (lang_extension)
364     {
365         case 1:
366             subtitle->attributes = HB_SUBTITLE_ATTR_NORMAL;
367             break;
368         case 2:
369             subtitle->attributes = HB_SUBTITLE_ATTR_LARGE;
370             strcat(subtitle->lang, " Large Type");
371             name = "Large Type";
372             break;
373         case 3:
374             subtitle->attributes = HB_SUBTITLE_ATTR_CHILDREN;
375             strcat(subtitle->lang, " Children");
376             name = "Children";
377             break;
378         case 5:
379             subtitle->attributes = HB_SUBTITLE_ATTR_CC;
380             strcat(subtitle->lang, " Closed Caption");
381             name = "Closed Caption";
382             break;
383         case 6:
384             subtitle->attributes = HB_SUBTITLE_ATTR_CC | HB_SUBTITLE_ATTR_LARGE;
385             strcat(subtitle->lang, " Closed Caption, Large Type");
386             name = "Closed Caption, Large Type";
387             break;
388         case 7:
389             subtitle->attributes = HB_SUBTITLE_ATTR_CC |
390                                    HB_SUBTITLE_ATTR_CHILDREN;
391             strcat(subtitle->lang, " Closed Caption, Children");
392             name = "Closed Caption, Children";
393             break;
394         case 9:
395             subtitle->attributes = HB_SUBTITLE_ATTR_FORCED;
396             strcat(subtitle->lang, " Forced");
397             break;
398         case 13:
399             subtitle->attributes = HB_SUBTITLE_ATTR_COMMENTARY;
400             strcat(subtitle->lang, " Director's Commentary");
401             name = "Commentary";
402             break;
403         case 14:
404             subtitle->attributes = HB_SUBTITLE_ATTR_COMMENTARY |
405                                    HB_SUBTITLE_ATTR_LARGE;
406             strcat(subtitle->lang, " Director's Commentary, Large Type");
407             name = "Commentary, Large Type";
408             break;
409         case 15:
410             subtitle->attributes = HB_SUBTITLE_ATTR_COMMENTARY |
411                                    HB_SUBTITLE_ATTR_CHILDREN;
412             strcat(subtitle->lang, " Director's Commentary, Children");
413             name = "Commentary, Children";
414         default:
415             subtitle->attributes = HB_SUBTITLE_ATTR_UNKNOWN;
416             break;
417     }
418     if (name != NULL)
419     {
420         subtitle->name = strdup(name);
421     }
422     switch (style)
423     {
424         case HB_VOBSUB_STYLE_4_3:
425             subtitle->attributes |= HB_SUBTITLE_ATTR_4_3;
426             strcat(subtitle->lang, " (4:3)");
427             break;
428         case HB_VOBSUB_STYLE_WIDE:
429             subtitle->attributes |= HB_SUBTITLE_ATTR_WIDE;
430             strcat(subtitle->lang, " (Wide Screen)");
431             break;
432         case HB_VOBSUB_STYLE_LETTERBOX:
433             subtitle->attributes |= HB_SUBTITLE_ATTR_LETTERBOX;
434             strcat(subtitle->lang, " (Letterbox)");
435             break;
436         case HB_VOBSUB_STYLE_PANSCAN:
437             subtitle->attributes |= HB_SUBTITLE_ATTR_PANSCAN;
438             strcat(subtitle->lang, " (Pan & Scan)");
439             break;
440     }
441     strcat(subtitle->lang, " [");
442     strcat(subtitle->lang, hb_subsource_name(subtitle->source));
443     strcat(subtitle->lang, "]");
444 
445     hb_log("scan: id=0x%x, lang=%s, 3cc=%s ext=%i", subtitle->id,
446            subtitle->lang, subtitle->iso639_2, lang_extension);
447 
448     hb_list_add(list_subtitle, subtitle);
449 }
450 
451 /***********************************************************************
452  * hb_dvdnav_title_scan
453  **********************************************************************/
hb_dvdnav_title_scan(hb_dvd_t * e,int t,uint64_t min_duration)454 static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t, uint64_t min_duration )
455 {
456 
457     hb_dvdnav_t      * d = &(e->dvdnav);
458     hb_title_t       * title;
459     int                pgcn, i;
460     pgc_t            * pgc;
461     hb_chapter_t     * chapter;
462     hb_dvd_chapter_t * dvd_chapter;
463     int                count;
464     const char       * title_string;
465     char               name[1024];
466     unsigned char      unused[1024];
467     const char       * codec_name;
468 
469     hb_log( "scan: scanning title %d", t );
470 
471     title = hb_title_init( d->path, t );
472     title->type = HB_DVD_TYPE;
473     if (dvdnav_get_title_string(d->dvdnav, &title_string) == DVDNAV_STATUS_OK)
474     {
475         title->name = strdup(title_string);
476     }
477 
478     if (title->name == NULL || title->name[0] == 0)
479     {
480         free((char*)title->name);
481         if (DVDUDFVolumeInfo(d->reader, name, sizeof(name),
482                              unused, sizeof(unused)))
483         {
484             char * p_cur, * p_last = d->path;
485             for( p_cur = d->path; *p_cur; p_cur++ )
486             {
487                 if( IS_DIR_SEP(p_cur[0]) && p_cur[1] )
488                 {
489                     p_last = &p_cur[1];
490                 }
491             }
492             title->name = strdup(p_last);
493             char *dot_term = strrchr(title->name, '.');
494             if (dot_term)
495                 *dot_term = '\0';
496         }
497         else
498         {
499             title->name = strdup(name);
500         }
501     }
502 
503     if (!TitleOpenIfo(d, t))
504     {
505         goto fail;
506     }
507 
508     /* ignore titles with bogus cell addresses so we don't abort later
509      ** in libdvdread. */
510     for ( i = 0; i < d->ifo->vts_c_adt->nr_of_vobs; ++i)
511     {
512         if( (d->ifo->vts_c_adt->cell_adr_table[i].start_sector & 0xffffff ) ==
513             0xffffff )
514         {
515             hb_log( "scan: cell_adr_table[%d].start_sector invalid (0x%x) "
516                     "- skipping title", i,
517                     d->ifo->vts_c_adt->cell_adr_table[i].start_sector );
518             goto fail;
519         }
520         if( (d->ifo->vts_c_adt->cell_adr_table[i].last_sector & 0xffffff ) ==
521             0xffffff )
522         {
523             hb_log( "scan: cell_adr_table[%d].last_sector invalid (0x%x) "
524                     "- skipping title", i,
525                     d->ifo->vts_c_adt->cell_adr_table[i].last_sector );
526             goto fail;
527         }
528     }
529 
530     if (global_verbosity_level == 4)
531     {
532         ifo_print( d->reader, d->vts );
533     }
534 
535     /* Get duration */
536     title->duration = d->duration;
537     title->hours    =   title->duration / 90000  / 3600;
538     title->minutes  = ((title->duration / 90000) % 3600) / 60;
539     title->seconds  = ( title->duration / 90000) % 60;
540 
541     hb_log( "scan: duration is %02d:%02d:%02d (%"PRId64" ms)",
542             title->hours, title->minutes, title->seconds,
543             title->duration / 90 );
544 
545     /* ignore titles under 10 seconds because they're often stills or
546      * clips with no audio & our preview code doesn't currently handle
547      * either of these. */
548     if (title->duration < min_duration)
549     {
550         hb_log( "scan: ignoring title (too short)" );
551         goto fail;
552     }
553 
554     /* Get pgc */
555     if (d->pgcn < 1                                 ||
556         d->pgcn > d->ifo->vts_pgcit->nr_of_pgci_srp ||
557         d->pgcn >= MAX_PGCN)
558     {
559         hb_log( "invalid PGC ID %d for title %d, skipping", d->pgcn, t );
560         goto fail;
561     }
562 
563     // Check all pgc's for validity
564     uint32_t pgcn_map[MAX_PGCN/32];
565     pgcn = d->pgcn;
566     PgcWalkInit( pgcn_map );
567     do
568     {
569         pgc = d->ifo->vts_pgcit->pgci_srp[pgcn-1].pgc;
570 
571         if (!pgc || !pgc->program_map)
572         {
573             hb_log( "scan: pgc not valid, skipping" );
574             goto fail;
575         }
576 
577         if (pgc->cell_playback == NULL)
578         {
579             hb_log( "invalid PGC cell_playback table for title %d, skipping", t );
580             goto fail;
581         }
582     } while ((pgcn = NextPgcn(d->ifo, pgcn, pgcn_map)) != 0);
583 
584     pgc = d->ifo->vts_pgcit->pgci_srp[d->pgcn-1].pgc;
585 
586     hb_log("pgc_id: %d, pgn: %d: pgc: %p", d->pgcn, d->pgn, pgc);
587     if (d->pgn > pgc->nr_of_programs)
588     {
589         hb_log( "invalid PGN %d for title %d, skipping", d->pgn, t );
590         goto fail;
591     }
592 
593     /* Detect languages */
594     for (i = 0; i < d->ifo->vtsi_mat->nr_of_vts_audio_streams; i++)
595     {
596         int audio_format, lang_code, lang_extension, audio_control, position, j;
597         hb_audio_t * audio, * audio_tmp;
598         iso639_lang_t * lang;
599 
600         hb_log( "scan: checking audio %d", i + 1 );
601 
602         audio = calloc( sizeof( hb_audio_t ), 1 );
603 
604         audio_format   = d->ifo->vtsi_mat->vts_audio_attr[i].audio_format;
605         lang_code      = d->ifo->vtsi_mat->vts_audio_attr[i].lang_code;
606         lang_extension = d->ifo->vtsi_mat->vts_audio_attr[i].code_extension;
607         audio_control  =
608             d->ifo->vts_pgcit->pgci_srp[d->pgcn-1].pgc->audio_control[i];
609 
610         if (!(audio_control & 0x8000))
611         {
612             hb_log( "scan: audio channel is not active" );
613             free( audio );
614             continue;
615         }
616 
617         position = ( audio_control & 0x7F00 ) >> 8;
618 
619         switch( audio_format )
620         {
621             case 0x00:
622                 audio->id    = ( ( 0x80 + position ) << 8 ) | 0xbd;
623                 audio->config.in.codec = HB_ACODEC_AC3;
624                 audio->config.in.codec_param = AV_CODEC_ID_AC3;
625                 codec_name = "AC3";
626                 break;
627 
628             case 0x02:
629             case 0x03:
630                 audio->id    = 0xc0 + position;
631                 audio->config.in.codec = HB_ACODEC_FFMPEG;
632                 audio->config.in.codec_param = AV_CODEC_ID_MP2;
633                 codec_name = "MPEG";
634                 break;
635 
636             case 0x04:
637                 audio->id    = ( ( 0xa0 + position ) << 8 ) | 0xbd;
638                 audio->config.in.codec = HB_ACODEC_LPCM;
639                 codec_name = "LPCM";
640                 break;
641 
642             case 0x06:
643                 audio->id    = ( ( 0x88 + position ) << 8 ) | 0xbd;
644                 audio->config.in.codec = HB_ACODEC_DCA;
645                 audio->config.in.codec_param = AV_CODEC_ID_DTS;
646                 codec_name = "DTS";
647                 break;
648 
649             default:
650                 audio->id    = 0;
651                 audio->config.in.codec = 0;
652                 codec_name = "Unknown";
653                 hb_log( "scan: unknown audio codec (%x)",
654                         audio_format );
655                 break;
656         }
657         if( !audio->id )
658         {
659             continue;
660         }
661         const char * name = NULL;
662         switch ( lang_extension )
663         {
664             case 1:
665                 audio->config.lang.attributes = HB_AUDIO_ATTR_NORMAL;
666                 break;
667             case 2:
668                 audio->config.lang.attributes = HB_AUDIO_ATTR_VISUALLY_IMPAIRED;
669                 name = "Visually Impaired";
670                 break;
671             case 3:
672                 audio->config.lang.attributes = HB_AUDIO_ATTR_COMMENTARY;
673                 name = "Commentary";
674                 break;
675             case 4:
676                 audio->config.lang.attributes = HB_AUDIO_ATTR_ALT_COMMENTARY;
677                 name = "Commentary";
678                 break;
679             default:
680                 audio->config.lang.attributes = HB_AUDIO_ATTR_NONE;
681                 break;
682         }
683         if (name != NULL)
684         {
685             audio->config.in.name = strdup(name);
686         }
687 
688         /* Check for duplicate tracks */
689         audio_tmp = NULL;
690         for( j = 0; j < hb_list_count( title->list_audio ); j++ )
691         {
692             audio_tmp = hb_list_item( title->list_audio, j );
693             if( audio->id == audio_tmp->id )
694             {
695                 break;
696             }
697             audio_tmp = NULL;
698         }
699         if( audio_tmp )
700         {
701             hb_log( "scan: duplicate audio track" );
702             free( audio );
703             continue;
704         }
705 
706         lang = lang_for_code( lang_code );
707 
708 
709         snprintf( audio->config.lang.simple,
710                   sizeof( audio->config.lang.simple ), "%s",
711                   strlen( lang->native_name ) ? lang->native_name : lang->eng_name );
712         snprintf( audio->config.lang.iso639_2,
713                   sizeof( audio->config.lang.iso639_2 ), "%s", lang->iso639_2 );
714 
715         hb_log("scan: id=0x%x, lang=%s (%s), 3cc=%s ext=%i", audio->id,
716                audio->config.lang.simple, codec_name,
717                audio->config.lang.iso639_2, lang_extension);
718 
719         audio->config.in.track        = i;
720         audio->config.in.timebase.num = 1;
721         audio->config.in.timebase.den = 90000;
722 
723         hb_list_add( title->list_audio, audio );
724     }
725 
726     /* Check for subtitles */
727     for( i = 0; i < d->ifo->vtsi_mat->nr_of_vts_subp_streams; i++ )
728     {
729         int             spu_control, pos, lang_ext = 0;
730         iso639_lang_t * lang;
731 
732         hb_log( "scan: checking subtitle %d", i + 1 );
733 
734         // spu_control
735         // 0x80000000 - Subtitle enabled
736         // 0x1f000000 - Position mask for 4:3 aspect subtitle track
737         // 0x001f0000 - Position mask for Wide Screen subtitle track
738         // 0x00001f00 - Position mask for Letterbox subtitle track
739         // 0x0000001f - Position mask for Pan&Scan subtitle track
740         spu_control =
741             d->ifo->vts_pgcit->pgci_srp[d->pgcn-1].pgc->subp_control[i];
742 
743         if( !( spu_control & 0x80000000 ) )
744         {
745             hb_log( "scan: subtitle channel is not active" );
746             continue;
747         }
748 
749         lang_ext = d->ifo->vtsi_mat->vts_subp_attr[i].code_extension;
750         lang     = lang_for_code(d->ifo->vtsi_mat->vts_subp_attr[i].lang_code);
751 
752         // display_aspect_ratio
753         // 0     = 4:3
754         // 3     = 16:9
755         // other = invalid
756         if (d->ifo->vtsi_mat->vts_video_attr.display_aspect_ratio)
757         {
758             // Add Wide Screen subtitle.
759             pos = (spu_control >> 16) & 0x1F;
760             add_subtitle(title->list_subtitle, pos, lang, lang_ext,
761                 (uint8_t*)d->ifo->vts_pgcit->pgci_srp[d->pgcn-1].pgc->palette,
762                 HB_VOBSUB_STYLE_WIDE);
763 
764             // permitted_df
765             // 1 - Letterbox not permitted
766             // 2 - Pan&Scan not permitted
767             // 3 - Letterbox and Pan&Scan not permitted
768             if (!(d->ifo->vtsi_mat->vts_video_attr.permitted_df & 1))
769             {
770                 // Letterbox permitted.  Add Letterbox subtitle.
771                 pos = (spu_control >> 8) & 0x1F;
772                 add_subtitle(title->list_subtitle, pos, lang, lang_ext,
773                     (uint8_t*)d->ifo->vts_pgcit->pgci_srp[d->pgcn-1].pgc->palette,
774                     HB_VOBSUB_STYLE_LETTERBOX);
775             }
776             if (!(d->ifo->vtsi_mat->vts_video_attr.permitted_df & 2))
777             {
778                 // Pan&Scan permitted.  Add Pan&Scan subtitle.
779                 pos = spu_control & 0x1F;
780                 add_subtitle(title->list_subtitle, pos, lang, lang_ext,
781                     (uint8_t*)d->ifo->vts_pgcit->pgci_srp[d->pgcn-1].pgc->palette,
782                     HB_VOBSUB_STYLE_PANSCAN);
783             }
784         }
785         else
786         {
787             pos = (spu_control >> 24) & 0x1F;
788             add_subtitle(title->list_subtitle, pos, lang, lang_ext,
789                 (uint8_t*)d->ifo->vts_pgcit->pgci_srp[d->pgcn-1].pgc->palette,
790                 HB_VOBSUB_STYLE_4_3);
791         }
792     }
793 
794     /* Chapters */
795     count = hb_list_count(d->list_dvd_chapter);
796     hb_log( "scan: title %d has %d chapters", t, count );
797     for (i = 0; i < count; i++)
798     {
799         char chapter_title[80];
800 
801         dvd_chapter       = hb_list_item(d->list_dvd_chapter, i);
802         chapter           = calloc(sizeof( hb_chapter_t ), 1);
803         chapter->index    = i + 1;
804         chapter->duration = dvd_chapter->duration;
805 
806         sprintf(chapter_title, "Chapter %d", chapter->index);
807         hb_chapter_set_title(chapter, chapter_title);
808 
809         hb_list_add( title->list_chapter, chapter );
810 
811         int seconds       = ( chapter->duration + 45000 ) / 90000;
812         chapter->hours    = ( seconds / 3600 );
813         chapter->minutes  = ( seconds % 3600 ) / 60;
814         chapter->seconds  = ( seconds % 60 );
815 
816         hb_log( "scan: chap %d, %"PRId64" ms",
817                 chapter->index, chapter->duration / 90 );
818     }
819 
820     /* Get aspect. We don't get width/height/rate infos here as
821        they tend to be wrong */
822     switch (d->ifo->vtsi_mat->vts_video_attr.display_aspect_ratio)
823     {
824         case 0:
825             title->container_dar.num = 4;
826             title->container_dar.den = 3;
827             break;
828         case 3:
829             title->container_dar.num = 16;
830             title->container_dar.den = 9;
831             break;
832         default:
833             hb_log( "scan: unknown aspect" );
834             goto fail;
835     }
836 
837     switch (d->ifo->vtsi_mat->vts_video_attr.mpeg_version)
838     {
839         case 0:
840             title->video_codec       = WORK_DECAVCODECV;
841             title->video_codec_param = AV_CODEC_ID_MPEG1VIDEO;
842             break;
843         case 1:
844             title->video_codec       = WORK_DECAVCODECV;
845             title->video_codec_param = AV_CODEC_ID_MPEG2VIDEO;
846             break;
847         default:
848             hb_log("scan: unknown/reserved MPEG version %d",
849                     d->ifo->vtsi_mat->vts_video_attr.mpeg_version);
850             title->video_codec       = WORK_DECAVCODECV;
851             title->video_codec_param = AV_CODEC_ID_MPEG2VIDEO;
852             break;
853     }
854 
855     hb_log("scan: aspect = %d:%d",
856            title->container_dar.num, title->container_dar.den);
857 
858     /* This title is ok so far */
859     goto cleanup;
860 
861 fail:
862     hb_title_close( &title );
863 
864 cleanup:
865     TitleCloseIfo(d);
866 
867     return title;
868 }
869 
870 /***********************************************************************
871  * hb_dvdnav_title_scan
872  **********************************************************************/
find_title(hb_list_t * list_title,int title)873 static int find_title( hb_list_t * list_title, int title )
874 {
875     int ii;
876 
877     for ( ii = 0; ii < hb_list_count( list_title ); ii++ )
878     {
879         hb_title_t * hbtitle = hb_list_item( list_title, ii );
880         if ( hbtitle->index == title )
881             return ii;
882     }
883     return -1;
884 }
885 
skip_to_menu(dvdnav_t * dvdnav,int blocks)886 static int skip_to_menu( dvdnav_t * dvdnav, int blocks )
887 {
888     int ii;
889     int result, event, len;
890     uint8_t buf[HB_DVD_READ_BUFFER_SIZE];
891 
892     for ( ii = 0; ii < blocks; ii++ )
893     {
894         result = dvdnav_get_next_block( dvdnav, buf, &event, &len );
895         if ( result == DVDNAV_STATUS_ERR )
896         {
897             hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(dvdnav));
898             return 0;
899         }
900         switch ( event )
901         {
902         case DVDNAV_BLOCK_OK:
903             break;
904 
905         case DVDNAV_CELL_CHANGE:
906         {
907         } break;
908 
909         case DVDNAV_STILL_FRAME:
910         {
911             dvdnav_still_event_t *event;
912             event = (dvdnav_still_event_t*)buf;
913             dvdnav_still_skip( dvdnav );
914             if ( event->length == 255 )
915             {
916                 // Infinite still. Can't be the main feature unless
917                 // you like watching paint dry.
918                 return 0;
919             }
920         } break;
921 
922         case DVDNAV_WAIT:
923             dvdnav_wait_skip( dvdnav );
924             break;
925 
926         case DVDNAV_STOP:
927             return 0;
928 
929         case DVDNAV_HOP_CHANNEL:
930             break;
931 
932         case DVDNAV_NAV_PACKET:
933         {
934             pci_t *pci = dvdnav_get_current_nav_pci( dvdnav );
935             if ( pci == NULL ) break;
936 
937             int buttons = pci->hli.hl_gi.btn_ns;
938 
939             int title, part;
940             result = dvdnav_current_title_info( dvdnav, &title, &part );
941             if (result != DVDNAV_STATUS_OK)
942             {
943                 hb_log("dvdnav title info: %s", dvdnav_err_to_string(dvdnav));
944             }
945             else if ( title == 0 && buttons > 0 )
946             {
947                 // Check button activation duration to see if this
948                 // isn't another fake menu.
949                 if ( pci->hli.hl_gi.btn_se_e_ptm - pci->hli.hl_gi.hli_s_ptm >
950                      15 * 90000 )
951                 {
952                     // Found what appears to be a good menu.
953                     return 1;
954                 }
955             }
956         } break;
957 
958         case DVDNAV_VTS_CHANGE:
959         {
960             dvdnav_vts_change_event_t *event;
961             event = (dvdnav_vts_change_event_t*)buf;
962             // Some discs initialize the vts with the "first play" item
963             // and some don't seem to.  So if we see it is uninitialized,
964             // set it.
965             if ( event->new_vtsN <= 0 )
966                 result = dvdnav_title_play( dvdnav, 1 );
967         } break;
968 
969         case DVDNAV_HIGHLIGHT:
970             break;
971 
972         case DVDNAV_AUDIO_STREAM_CHANGE:
973             break;
974 
975         case DVDNAV_SPU_STREAM_CHANGE:
976             break;
977 
978         case DVDNAV_SPU_CLUT_CHANGE:
979             break;
980 
981         case DVDNAV_NOP:
982             break;
983 
984         default:
985             break;
986         }
987     }
988     return 0;
989 }
990 
try_button(dvdnav_t * dvdnav,int button,hb_list_t * list_title)991 static int try_button( dvdnav_t * dvdnav, int button, hb_list_t * list_title )
992 {
993     int result, event, len;
994     uint8_t buf[HB_DVD_READ_BUFFER_SIZE];
995     int ii, jj;
996     int32_t cur_title = 0, title, part;
997     uint64_t longest_duration = 0;
998     int longest = -1;
999 
1000     pci_t *pci = dvdnav_get_current_nav_pci( dvdnav );
1001 
1002     result = dvdnav_button_select_and_activate( dvdnav, pci, button + 1 );
1003     if (result != DVDNAV_STATUS_OK)
1004     {
1005         hb_log("dvdnav_button_select_and_activate: %s", dvdnav_err_to_string(dvdnav));
1006     }
1007 
1008     result = dvdnav_current_title_info( dvdnav, &title, &part );
1009     if (result != DVDNAV_STATUS_OK)
1010         hb_log("dvdnav cur title info: %s", dvdnav_err_to_string(dvdnav));
1011 
1012     cur_title = title;
1013 
1014     for (jj = 0; jj < 10; jj++)
1015     {
1016         for (ii = 0; ii < 2000; ii++)
1017         {
1018             result = dvdnav_get_next_block( dvdnav, buf, &event, &len );
1019             if ( result == DVDNAV_STATUS_ERR )
1020             {
1021                 hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(dvdnav));
1022                 goto done;
1023             }
1024             switch ( event )
1025             {
1026             case DVDNAV_BLOCK_OK:
1027                 break;
1028 
1029             case DVDNAV_CELL_CHANGE:
1030             {
1031                 result = dvdnav_current_title_info( dvdnav, &title, &part );
1032                 if (result != DVDNAV_STATUS_OK)
1033                     hb_log("dvdnav title info: %s", dvdnav_err_to_string(dvdnav));
1034 
1035                 cur_title = title;
1036                 // Note, some "fake" titles have long advertised durations
1037                 // but then jump to the real title early in playback.
1038                 // So keep reading after finding a long title to detect
1039                 // such cases.
1040             } break;
1041 
1042             case DVDNAV_STILL_FRAME:
1043             {
1044                 dvdnav_still_event_t *event;
1045                 event = (dvdnav_still_event_t*)buf;
1046                 dvdnav_still_skip( dvdnav );
1047                 if ( event->length == 255 )
1048                 {
1049                     // Infinite still. Can't be the main feature unless
1050                     // you like watching paint dry.
1051                     goto done;
1052                 }
1053             } break;
1054 
1055             case DVDNAV_WAIT:
1056                 dvdnav_wait_skip( dvdnav );
1057                 break;
1058 
1059             case DVDNAV_STOP:
1060                 goto done;
1061 
1062             case DVDNAV_HOP_CHANNEL:
1063                 break;
1064 
1065             case DVDNAV_NAV_PACKET:
1066             {
1067             } break;
1068 
1069             case DVDNAV_VTS_CHANGE:
1070             {
1071                 result = dvdnav_current_title_info( dvdnav, &title, &part );
1072                 if (result != DVDNAV_STATUS_OK)
1073                     hb_log("dvdnav title info: %s", dvdnav_err_to_string(dvdnav));
1074 
1075                 cur_title = title;
1076                 // Note, some "fake" titles have long advertised durations
1077                 // but then jump to the real title early in playback.
1078                 // So keep reading after finding a long title to detect
1079                 // such cases.
1080             } break;
1081 
1082             case DVDNAV_HIGHLIGHT:
1083                 break;
1084 
1085             case DVDNAV_AUDIO_STREAM_CHANGE:
1086                 break;
1087 
1088             case DVDNAV_SPU_STREAM_CHANGE:
1089                 break;
1090 
1091             case DVDNAV_SPU_CLUT_CHANGE:
1092                 break;
1093 
1094             case DVDNAV_NOP:
1095                 break;
1096 
1097             default:
1098                 break;
1099             }
1100         }
1101         // Check if the current title is long enough to qualify
1102         // as the main feature.
1103         if ( cur_title > 0 )
1104         {
1105             hb_title_t * hbtitle;
1106             int index;
1107             index = find_title( list_title, cur_title );
1108             hbtitle = hb_list_item( list_title, index );
1109             if ( hbtitle != NULL )
1110             {
1111                 if ( hbtitle->duration / 90000 > 10 * 60 )
1112                 {
1113                     hb_deep_log( 3, "dvdnav: Found candidate feature title %d duration %02d:%02d:%02d on button %d",
1114                     cur_title, hbtitle->hours, hbtitle->minutes,
1115                     hbtitle->seconds, button+1 );
1116                     return cur_title;
1117                 }
1118                 if ( hbtitle->duration > longest_duration )
1119                 {
1120                     longest_duration = hbtitle->duration;
1121                     longest = title;
1122                 }
1123             }
1124             // Some titles have long lead-ins. Try skipping it.
1125             dvdnav_next_pg_search( dvdnav );
1126         }
1127     }
1128 
1129 done:
1130     if ( longest != -1 )
1131     {
1132         hb_title_t * hbtitle;
1133         int index;
1134         index = find_title( list_title, longest );
1135         hbtitle = hb_list_item( list_title, index );
1136         if ( hbtitle != NULL )
1137         {
1138             hb_deep_log( 3, "dvdnav: Found candidate feature title %d duration %02d:%02d:%02d on button %d",
1139             longest, hbtitle->hours, hbtitle->minutes,
1140             hbtitle->seconds, button+1 );
1141         }
1142     }
1143     return longest;
1144 }
1145 
try_menu(hb_dvdnav_t * d,hb_list_t * list_title,DVDMenuID_t menu,uint64_t fallback_duration)1146 static int try_menu(
1147     hb_dvdnav_t * d,
1148     hb_list_t * list_title,
1149     DVDMenuID_t menu,
1150     uint64_t fallback_duration )
1151 {
1152     int result, event, len;
1153     uint8_t buf[HB_DVD_READ_BUFFER_SIZE];
1154     int ii, jj;
1155     int32_t cur_title, title, part;
1156     uint64_t longest_duration = 0;
1157     int longest = -1;
1158 
1159     // A bit of a hack here.  Abusing Escape menu to mean use whatever
1160     // current menu is already set.
1161     if ( menu != DVD_MENU_Escape )
1162     {
1163         result = dvdnav_menu_call( d->dvdnav, menu );
1164         if ( result != DVDNAV_STATUS_OK )
1165         {
1166             // Sometimes the "first play" item doesn't initialize the
1167             // initial VTS. So do it here.
1168             result = dvdnav_title_play( d->dvdnav, 1 );
1169             result = dvdnav_menu_call( d->dvdnav, menu );
1170             if ( result != DVDNAV_STATUS_OK )
1171             {
1172                 hb_error("dvdnav: Can not set dvd menu, %s", dvdnav_err_to_string(d->dvdnav));
1173                 goto done;
1174             }
1175         }
1176     }
1177 
1178     result = dvdnav_current_title_info( d->dvdnav, &title, &part );
1179     if (result != DVDNAV_STATUS_OK)
1180         hb_log("dvdnav title info: %s", dvdnav_err_to_string(d->dvdnav));
1181 
1182     cur_title = title;
1183 
1184     for (jj = 0; jj < 4; jj++)
1185     {
1186         for (ii = 0; ii < 4000; ii++)
1187         {
1188             result = dvdnav_get_next_block( d->dvdnav, buf, &event, &len );
1189             if ( result == DVDNAV_STATUS_ERR )
1190             {
1191                 hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav));
1192                 goto done;
1193             }
1194             switch ( event )
1195             {
1196             case DVDNAV_BLOCK_OK:
1197                 break;
1198 
1199             case DVDNAV_CELL_CHANGE:
1200             {
1201                 result = dvdnav_current_title_info( d->dvdnav, &title, &part );
1202                 if (result != DVDNAV_STATUS_OK)
1203                     hb_log("dvdnav title info: %s", dvdnav_err_to_string(d->dvdnav));
1204                 cur_title = title;
1205             } break;
1206 
1207             case DVDNAV_STILL_FRAME:
1208             {
1209                 dvdnav_still_event_t *event;
1210                 event = (dvdnav_still_event_t*)buf;
1211                 dvdnav_still_skip( d->dvdnav );
1212                 if ( event->length == 255 )
1213                 {
1214                     // Infinite still.  There won't be any menus after this.
1215                     goto done;
1216                 }
1217             } break;
1218 
1219             case DVDNAV_WAIT:
1220                 dvdnav_wait_skip( d->dvdnav );
1221                 break;
1222 
1223             case DVDNAV_STOP:
1224                 goto done;
1225 
1226             case DVDNAV_HOP_CHANNEL:
1227                 break;
1228 
1229             case DVDNAV_NAV_PACKET:
1230             {
1231                 pci_t *pci = dvdnav_get_current_nav_pci( d->dvdnav );
1232                 int kk;
1233                 int buttons;
1234                 if ( pci == NULL ) break;
1235 
1236                 buttons = pci->hli.hl_gi.btn_ns;
1237 
1238                 // If we are on a menu that has buttons and
1239                 // the button activation duration is long enough
1240                 // that this isn't another fake menu.
1241                 if ( cur_title == 0 && buttons > 0 &&
1242                      pci->hli.hl_gi.btn_se_e_ptm - pci->hli.hl_gi.hli_s_ptm >
1243                      15 * 90000 )
1244                 {
1245                     for (kk = 0; kk < buttons; kk++)
1246                     {
1247                         dvdnav_t *dvdnav_copy;
1248 
1249                         result = dvdnav_dup( &dvdnav_copy, d->dvdnav );
1250                         if (result != DVDNAV_STATUS_OK)
1251                         {
1252                             hb_log("dvdnav dup failed: %s", dvdnav_err_to_string(d->dvdnav));
1253                             goto done;
1254                         }
1255                         title = try_button( dvdnav_copy, kk, list_title );
1256                         dvdnav_free_dup( dvdnav_copy );
1257 
1258                         if ( title >= 0 )
1259                         {
1260                             hb_title_t * hbtitle;
1261                             int index;
1262                             index = find_title( list_title, title );
1263                             hbtitle = hb_list_item( list_title, index );
1264                             if ( hbtitle != NULL )
1265                             {
1266                                 if ( hbtitle->duration > longest_duration )
1267                                 {
1268                                     longest_duration = hbtitle->duration;
1269                                     longest = title;
1270                                     if ((float)fallback_duration * 0.75 < longest_duration)
1271                                         goto done;
1272                                 }
1273                             }
1274                         }
1275                     }
1276                     goto done;
1277                 }
1278             } break;
1279 
1280             case DVDNAV_VTS_CHANGE:
1281             {
1282                 result = dvdnav_current_title_info( d->dvdnav, &title, &part );
1283                 if (result != DVDNAV_STATUS_OK)
1284                     hb_log("dvdnav title info: %s", dvdnav_err_to_string(d->dvdnav));
1285                 cur_title = title;
1286             } break;
1287 
1288             case DVDNAV_HIGHLIGHT:
1289                 break;
1290 
1291             case DVDNAV_AUDIO_STREAM_CHANGE:
1292                 break;
1293 
1294             case DVDNAV_SPU_STREAM_CHANGE:
1295                 break;
1296 
1297             case DVDNAV_SPU_CLUT_CHANGE:
1298                 break;
1299 
1300             case DVDNAV_NOP:
1301                 break;
1302 
1303             default:
1304                 break;
1305             }
1306         }
1307         // Sometimes the menu is preceded by a intro that just
1308         // gets restarted when hitting the menu button. So
1309         // try skipping with the skip forward button.  Then
1310         // try hitting the menu again.
1311         if ( !(jj & 1) )
1312         {
1313             dvdnav_next_pg_search( d->dvdnav );
1314         }
1315         else
1316         {
1317             result = dvdnav_menu_call( d->dvdnav, menu );
1318         }
1319     }
1320 
1321 done:
1322     return longest;
1323 }
1324 
hb_dvdnav_main_feature(hb_dvd_t * e,hb_list_t * list_title)1325 static int hb_dvdnav_main_feature( hb_dvd_t * e, hb_list_t * list_title )
1326 {
1327     hb_dvdnav_t * d = &(e->dvdnav);
1328     int longest_root = -1;
1329     int longest_title = -1;
1330     int longest_fallback = 0;
1331     int ii;
1332     uint64_t longest_duration_root = 0;
1333     uint64_t longest_duration_title = 0;
1334     uint64_t longest_duration_fallback = 0;
1335     uint64_t avg_duration = 0;
1336     int avg_cnt = 0;
1337     hb_title_t * title;
1338     int index;
1339 
1340     hb_deep_log( 2, "dvdnav: Searching menus for main feature" );
1341     for ( ii = 0; ii < hb_list_count( list_title ); ii++ )
1342     {
1343         title = hb_list_item( list_title, ii );
1344         if ( title->duration > longest_duration_fallback )
1345         {
1346             longest_duration_fallback = title->duration;
1347             longest_fallback = title->index;
1348         }
1349         if ( title->duration > 90000LL * 60 * 30 )
1350         {
1351             avg_duration += title->duration;
1352             avg_cnt++;
1353         }
1354     }
1355     if ( avg_cnt )
1356         avg_duration /= avg_cnt;
1357 
1358     index = find_title( list_title, longest_fallback );
1359     title = hb_list_item( list_title, index );
1360     if ( title )
1361     {
1362         hb_deep_log( 2, "dvdnav: Longest title %d duration %02d:%02d:%02d",
1363                     longest_fallback, title->hours, title->minutes,
1364                     title->seconds );
1365     }
1366 
1367     dvdnav_reset( d->dvdnav );
1368     if ( skip_to_menu( d->dvdnav, 2000 ) )
1369     {
1370         longest_root = try_menu( d, list_title, DVD_MENU_Escape, longest_duration_fallback );
1371         if ( longest_root >= 0 )
1372         {
1373             index = find_title( list_title, longest_root );
1374             title = hb_list_item( list_title, index );
1375             if ( title )
1376             {
1377                 longest_duration_root = title->duration;
1378                 hb_deep_log( 2, "dvdnav: Found first-play title %d duration %02d:%02d:%02d",
1379                             longest_root, title->hours, title->minutes, title->seconds );
1380             }
1381         }
1382         else
1383         {
1384             hb_deep_log( 2, "dvdnav: No first-play menu title found" );
1385         }
1386     }
1387 
1388     if ( longest_root < 0 ||
1389          (float)longest_duration_fallback * 0.7 > longest_duration_root)
1390     {
1391         longest_root = try_menu( d, list_title, DVD_MENU_Root, longest_duration_fallback );
1392         if ( longest_root >= 0 )
1393         {
1394             index = find_title( list_title, longest_root );
1395             title = hb_list_item( list_title, index );
1396             if ( title )
1397             {
1398                 longest_duration_root = title->duration;
1399                 hb_deep_log( 2, "dvdnav: Found root title %d duration %02d:%02d:%02d",
1400                             longest_root, title->hours, title->minutes, title->seconds );
1401             }
1402         }
1403         else
1404         {
1405             hb_deep_log( 2, "dvdnav: No root menu title found" );
1406         }
1407     }
1408 
1409     if ( longest_root < 0 ||
1410          (float)longest_duration_fallback * 0.7 > longest_duration_root)
1411     {
1412         longest_title = try_menu( d, list_title, DVD_MENU_Title, longest_duration_fallback );
1413         if ( longest_title >= 0 )
1414         {
1415             index = find_title( list_title, longest_title );
1416             title = hb_list_item( list_title, index );
1417             if ( title )
1418             {
1419                 longest_duration_title = title->duration;
1420                 hb_deep_log( 2, "dvdnav: found title %d duration %02d:%02d:%02d",
1421                             longest_title, title->hours, title->minutes,
1422                             title->seconds );
1423             }
1424         }
1425         else
1426         {
1427             hb_deep_log( 2, "dvdnav: No title menu title found" );
1428         }
1429     }
1430 
1431     uint64_t longest_duration;
1432     int longest;
1433 
1434     if ( longest_duration_root > longest_duration_title )
1435     {
1436         longest_duration = longest_duration_root;
1437         longest = longest_root;
1438     }
1439     else
1440     {
1441         longest_duration = longest_duration_title;
1442         longest = longest_title;
1443     }
1444     if ((float)longest_duration_fallback * 0.7 > longest_duration &&
1445         longest_duration < 90000LL * 60 * 30 )
1446     {
1447         float factor = (float)avg_duration / longest_duration;
1448         if ( factor > 1 )
1449             factor = 1 / factor;
1450         if ( avg_cnt > 10 && factor < 0.7 )
1451         {
1452             longest = longest_fallback;
1453             hb_deep_log( 2, "dvdnav: Using longest title %d", longest );
1454         }
1455     }
1456     return longest;
1457 }
1458 
1459 /***********************************************************************
1460  * hb_dvdnav_start
1461  ***********************************************************************
1462  * Title and chapter start at 1
1463  **********************************************************************/
hb_dvdnav_start(hb_dvd_t * e,hb_title_t * title,int c)1464 static int hb_dvdnav_start( hb_dvd_t * e, hb_title_t *title, int c )
1465 {
1466     hb_dvdnav_t * d = &(e->dvdnav);
1467     int t = title->index;
1468     hb_dvd_chapter_t *chapter;
1469     dvdnav_status_t result;
1470 
1471     if ( d->stopped && !hb_dvdnav_reset(d) )
1472     {
1473         return 0;
1474     }
1475     if (!TitleOpenIfo(d, t))
1476     {
1477         return 0;
1478     }
1479     dvdnav_reset( d->dvdnav );
1480     chapter = hb_list_item( d->list_dvd_chapter, c - 1);
1481     if (chapter != NULL)
1482         result = dvdnav_program_play(d->dvdnav, t, chapter->pgcn, chapter->pgn);
1483     else
1484         result = dvdnav_part_play(d->dvdnav, t, 1);
1485     if (result != DVDNAV_STATUS_OK)
1486     {
1487         hb_error( "dvd: dvdnav_*_play failed - %s",
1488                   dvdnav_err_to_string(d->dvdnav) );
1489         return 0;
1490     }
1491     d->stopped = 0;
1492     d->chapter = 0;
1493     d->cell = 0;
1494     return 1;
1495 }
1496 
1497 /***********************************************************************
1498  * hb_dvdnav_stop
1499  ***********************************************************************
1500  *
1501  **********************************************************************/
hb_dvdnav_stop(hb_dvd_t * e)1502 static void hb_dvdnav_stop( hb_dvd_t * e )
1503 {
1504 }
1505 
1506 /***********************************************************************
1507  * hb_dvdnav_seek
1508  ***********************************************************************
1509  *
1510  **********************************************************************/
hb_dvdnav_seek(hb_dvd_t * e,float f)1511 static int hb_dvdnav_seek( hb_dvd_t * e, float f )
1512 {
1513     hb_dvdnav_t * d = &(e->dvdnav);
1514     uint64_t sector = f * d->title_block_count;
1515     int result, event, len;
1516     uint8_t buf[HB_DVD_READ_BUFFER_SIZE];
1517     int done = 0, ii;
1518 
1519     if (d->stopped)
1520     {
1521         return 0;
1522     }
1523 
1524     // XXX the current version of libdvdnav can't seek outside the current
1525     // PGC. Check if the place we're seeking to is in a different
1526     // PGC. Position there & adjust the offset if so.
1527     uint64_t pgc_offset = 0;
1528     uint64_t chap_offset = 0;
1529     hb_dvd_chapter_t *pgc_change = hb_list_item(d->list_dvd_chapter, 0 );
1530     for ( ii = 0; ii < hb_list_count( d->list_dvd_chapter ); ++ii )
1531     {
1532         hb_dvd_chapter_t *chapter = hb_list_item( d->list_dvd_chapter, ii );
1533         uint64_t chap_len = chapter->block_end - chapter->block_start + 1;
1534 
1535         if ( chapter->pgcn != pgc_change->pgcn )
1536         {
1537             // this chapter's in a different pgc from the previous - note the
1538             // change so we can make sector offset's be pgc relative.
1539             pgc_offset = chap_offset;
1540             pgc_change = chapter;
1541         }
1542         if ( chap_offset <= sector && sector < chap_offset + chap_len )
1543         {
1544             // this chapter contains the sector we want - see if it's in a
1545             // different pgc than the one we're currently in.
1546             int32_t title, pgcn, pgn;
1547             if (dvdnav_current_title_program( d->dvdnav, &title, &pgcn, &pgn ) != DVDNAV_STATUS_OK)
1548                 hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(d->dvdnav));
1549             // If we find ourselves in a new title, it means a title
1550             // transition was made while reading data.  Jumping between
1551             // titles can cause the vm to get into a bad state.  So
1552             // reset the vm in this case.
1553             if ( d->title != title )
1554                 dvdnav_reset( d->dvdnav );
1555 
1556             if ( d->title != title || chapter->pgcn != pgcn )
1557             {
1558                 // this chapter is in a different pgc - switch to it.
1559                 if (dvdnav_program_play(d->dvdnav, d->title, chapter->pgcn, chapter->pgn) != DVDNAV_STATUS_OK)
1560                     hb_log("dvdnav prog play err: %s", dvdnav_err_to_string(d->dvdnav));
1561             }
1562             // seek sectors are pgc-relative so remove the pgc start sector.
1563             sector -= pgc_offset;
1564             break;
1565         }
1566         chap_offset += chap_len;
1567     }
1568 
1569     // dvdnav will not let you seek or poll current position
1570     // till it reaches a certain point in parsing.  so we
1571     // have to get blocks until we reach a cell
1572     // Put an arbitrary limit of 100 blocks on how long we search
1573     for (ii = 0; ii < 100 && !done; ii++)
1574     {
1575         result = dvdnav_get_next_block( d->dvdnav, buf, &event, &len );
1576         if ( result == DVDNAV_STATUS_ERR )
1577         {
1578             hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav));
1579             return 0;
1580         }
1581         switch ( event )
1582         {
1583         case DVDNAV_BLOCK_OK:
1584         case DVDNAV_CELL_CHANGE:
1585             done = 1;
1586             break;
1587 
1588         case DVDNAV_STILL_FRAME:
1589             dvdnav_still_skip( d->dvdnav );
1590             break;
1591 
1592         case DVDNAV_WAIT:
1593             dvdnav_wait_skip( d->dvdnav );
1594             break;
1595 
1596         case DVDNAV_STOP:
1597             hb_log("dvdnav: stop encountered during seek");
1598             d->stopped = 1;
1599             return 0;
1600 
1601         case DVDNAV_HOP_CHANNEL:
1602         case DVDNAV_NAV_PACKET:
1603         case DVDNAV_VTS_CHANGE:
1604         case DVDNAV_HIGHLIGHT:
1605         case DVDNAV_AUDIO_STREAM_CHANGE:
1606         case DVDNAV_SPU_STREAM_CHANGE:
1607         case DVDNAV_SPU_CLUT_CHANGE:
1608         case DVDNAV_NOP:
1609         default:
1610             break;
1611         }
1612     }
1613 
1614     if (dvdnav_sector_search(d->dvdnav, sector, SEEK_SET) != DVDNAV_STATUS_OK)
1615     {
1616         hb_error( "dvd: dvdnav_sector_search failed - %s",
1617                   dvdnav_err_to_string(d->dvdnav) );
1618         return 0;
1619     }
1620     d->chapter = 0;
1621     d->cell = 0;
1622     return 1;
1623 }
1624 
1625 /***********************************************************************
1626  * hb_dvdnav_read
1627  ***********************************************************************
1628  *
1629  **********************************************************************/
hb_dvdnav_read(hb_dvd_t * e)1630 static hb_buffer_t * hb_dvdnav_read( hb_dvd_t * e )
1631 {
1632     hb_dvdnav_t * d = &(e->dvdnav);
1633     int result, event, len;
1634     int chapter = 0;
1635     int error_count = 0;
1636     hb_buffer_t *b = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
1637 
1638     while ( 1 )
1639     {
1640         if (d->stopped)
1641         {
1642             hb_buffer_close( &b );
1643             return NULL;
1644         }
1645         result = dvdnav_get_next_block( d->dvdnav, b->data, &event, &len );
1646         if ( result == DVDNAV_STATUS_ERR )
1647         {
1648             hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav));
1649             if (dvdnav_sector_search(d->dvdnav, 1, SEEK_CUR) != DVDNAV_STATUS_OK)
1650             {
1651                 hb_error( "dvd: dvdnav_sector_search failed - %s",
1652                         dvdnav_err_to_string(d->dvdnav) );
1653                 hb_buffer_close( &b );
1654                 hb_set_work_error(d->h, HB_ERROR_READ);
1655                 return NULL;
1656             }
1657             error_count++;
1658             if (error_count > 500)
1659             {
1660                 hb_error("dvdnav: Error, too many consecutive read errors");
1661                 hb_buffer_close( &b );
1662                 hb_set_work_error(d->h, HB_ERROR_READ);
1663                 return NULL;
1664             }
1665             continue;
1666         }
1667         switch ( event )
1668         {
1669         case DVDNAV_BLOCK_OK:
1670             // We have received a regular block of the currently playing
1671             // MPEG stream.
1672             b->s.new_chap = chapter;
1673             chapter = 0;
1674             error_count = 0;
1675             return b;
1676 
1677         case DVDNAV_NOP:
1678             /*
1679             * Nothing to do here.
1680             */
1681             break;
1682 
1683         case DVDNAV_STILL_FRAME:
1684             /*
1685             * We have reached a still frame. A real player application
1686             * would wait the amount of time specified by the still's
1687             * length while still handling user input to make menus and
1688             * other interactive stills work. A length of 0xff means an
1689             * indefinite still which has to be skipped indirectly by some
1690             * user interaction.
1691             */
1692             dvdnav_still_skip( d->dvdnav );
1693             break;
1694 
1695         case DVDNAV_WAIT:
1696             /*
1697             * We have reached a point in DVD playback, where timing is
1698             * critical. Player application with internal fifos can
1699             * introduce state inconsistencies, because libdvdnav is
1700             * always the fifo's length ahead in the stream compared to
1701             * what the application sees. Such applications should wait
1702             * until their fifos are empty when they receive this type of
1703             * event.
1704             */
1705             dvdnav_wait_skip( d->dvdnav );
1706             break;
1707 
1708         case DVDNAV_SPU_CLUT_CHANGE:
1709             /*
1710             * Player applications should pass the new colour lookup table
1711             * to their SPU decoder
1712             */
1713             break;
1714 
1715         case DVDNAV_SPU_STREAM_CHANGE:
1716             /*
1717             * Player applications should inform their SPU decoder to
1718             * switch channels
1719             */
1720             break;
1721 
1722         case DVDNAV_AUDIO_STREAM_CHANGE:
1723             /*
1724             * Player applications should inform their audio decoder to
1725             * switch channels
1726             */
1727             break;
1728 
1729         case DVDNAV_HIGHLIGHT:
1730             /*
1731             * Player applications should inform their overlay engine to
1732             * highlight the given button
1733             */
1734             break;
1735 
1736         case DVDNAV_VTS_CHANGE:
1737             /*
1738             * Some status information like video aspect and video scale
1739             * permissions do not change inside a VTS. Therefore this
1740             * event can be used to query such information only when
1741             * necessary and update the decoding/displaying accordingly.
1742             */
1743             {
1744                 int tt = 0, pgcn = 0, pgn = 0;
1745 
1746                 dvdnav_current_title_program(d->dvdnav, &tt, &pgcn, &pgn);
1747                 if (tt != d->title)
1748                 {
1749                     // Transition to another title signals that we are done.
1750                     hb_buffer_close( &b );
1751                     hb_deep_log(2, "dvdnav: vts change, found next title");
1752                     if (error_count > 0)
1753                     {
1754                         // Last read attempt failed
1755                         hb_set_work_error(d->h, HB_ERROR_READ);
1756                     }
1757                     return NULL;
1758                 }
1759             }
1760             break;
1761 
1762         case DVDNAV_CELL_CHANGE:
1763             /*
1764             * Some status information like the current Title and Part
1765             * numbers do not change inside a cell. Therefore this event
1766             * can be used to query such information only when necessary
1767             * and update the decoding/displaying accordingly.
1768             */
1769             {
1770                 dvdnav_cell_change_event_t * cell_event;
1771                 int tt = 0, pgcn = 0, pgn = 0, c;
1772 
1773                 cell_event = (dvdnav_cell_change_event_t*)b->data;
1774 
1775                 dvdnav_current_title_program(d->dvdnav, &tt, &pgcn, &pgn);
1776                 if (tt != d->title)
1777                 {
1778                     // Transition to another title signals that we are done.
1779                     hb_buffer_close( &b );
1780                     hb_deep_log(2, "dvdnav: cell change, found next title");
1781                     if (error_count > 0)
1782                     {
1783                         // Last read attempt failed
1784                         hb_set_work_error(d->h, HB_ERROR_READ);
1785                     }
1786                     return NULL;
1787                 }
1788                 c = FindChapterIndex(d->list_dvd_chapter, pgcn, pgn);
1789                 if (c != d->chapter)
1790                 {
1791                     if (c < d->chapter)
1792                     {
1793                         // Some titles end with a 'link' back to the beginning so
1794                         // a transition to an earlier chapter means we're done.
1795                         hb_buffer_close( &b );
1796                         hb_deep_log(2, "dvdnav: cell change, previous chapter");
1797                         if (error_count > 0)
1798                         {
1799                             // Last read attempt failed
1800                             hb_set_work_error(d->h, HB_ERROR_READ);
1801                         }
1802                         return NULL;
1803                     }
1804                     chapter = d->chapter = c;
1805                 }
1806                 else if ( cell_event->cellN <= d->cell )
1807                 {
1808                     hb_buffer_close( &b );
1809                     hb_deep_log(2, "dvdnav: cell change, previous cell");
1810                     if (error_count > 0)
1811                     {
1812                         // Last read attempt failed
1813                         hb_set_work_error(d->h, HB_ERROR_READ);
1814                     }
1815                     return NULL;
1816                 }
1817                 d->cell = cell_event->cellN;
1818             }
1819             break;
1820 
1821         case DVDNAV_NAV_PACKET:
1822             /*
1823             * A NAV packet provides PTS discontinuity information, angle
1824             * linking information and button definitions for DVD menus.
1825             * Angles are handled completely inside libdvdnav. For the
1826             * menus to work, the NAV packet information has to be passed
1827             * to the overlay engine of the player so that it knows the
1828             * dimensions of the button areas.
1829             */
1830 
1831             // mpegdemux expects to get these.  I don't think it does
1832             // anything useful with them however.
1833             b->s.new_chap = chapter;
1834             return b;
1835 
1836             break;
1837 
1838         case DVDNAV_HOP_CHANNEL:
1839             /*
1840             * This event is issued whenever a non-seamless operation has
1841             * been executed. Applications with fifos should drop the
1842             * fifos content to speed up responsiveness.
1843             */
1844             break;
1845 
1846         case DVDNAV_STOP:
1847             /*
1848             * Playback should end here.
1849             */
1850             d->stopped = 1;
1851             hb_buffer_close( &b );
1852             hb_deep_log(2, "dvdnav: stop");
1853             if (error_count > 0)
1854             {
1855                 // Last read attempt failed
1856                 hb_set_work_error(d->h, HB_ERROR_READ);
1857             }
1858             return NULL;
1859 
1860         default:
1861             break;
1862         }
1863     }
1864     hb_buffer_close( &b );
1865     return NULL;
1866 }
1867 
1868 /***********************************************************************
1869  * hb_dvdnav_chapter
1870  ***********************************************************************
1871  * Returns in which chapter the next block to be read is.
1872  * Chapter numbers start at 1.
1873  **********************************************************************/
hb_dvdnav_chapter(hb_dvd_t * e)1874 static int hb_dvdnav_chapter( hb_dvd_t * e )
1875 {
1876     hb_dvdnav_t * d = &(e->dvdnav);
1877     int32_t t, pgcn, pgn;
1878     int32_t c;
1879 
1880     if (dvdnav_current_title_program(d->dvdnav, &t, &pgcn, &pgn) != DVDNAV_STATUS_OK)
1881     {
1882         return -1;
1883     }
1884     c = FindChapterIndex( d->list_dvd_chapter, pgcn, pgn );
1885     return c;
1886 }
1887 
1888 /***********************************************************************
1889  * hb_dvdnav_close
1890  ***********************************************************************
1891  * Closes and frees everything
1892  **********************************************************************/
hb_dvdnav_close(hb_dvd_t ** _d)1893 static void hb_dvdnav_close( hb_dvd_t ** _d )
1894 {
1895     hb_dvdnav_t      * d = &((*_d)->dvdnav);
1896 
1897     if (d->dvdnav) dvdnav_close( d->dvdnav );
1898     if (d->vmg)    ifoClose( d->vmg );
1899     TitleCloseIfo(d);
1900     if (d->reader) DVDClose( d->reader );
1901 
1902     free(d->path);
1903 
1904 
1905     free( d );
1906     *_d = NULL;
1907 }
1908 
1909 /***********************************************************************
1910  * hb_dvdnav_angle_count
1911  ***********************************************************************
1912  * Returns the number of angles supported.
1913  **********************************************************************/
hb_dvdnav_angle_count(hb_dvd_t * e)1914 static int hb_dvdnav_angle_count( hb_dvd_t * e )
1915 {
1916     hb_dvdnav_t * d = &(e->dvdnav);
1917     int current, angle_count;
1918 
1919     if (dvdnav_get_angle_info( d->dvdnav, &current, &angle_count) != DVDNAV_STATUS_OK)
1920     {
1921         hb_log("dvdnav_get_angle_info %s", dvdnav_err_to_string(d->dvdnav));
1922         angle_count = 1;
1923     }
1924     return angle_count;
1925 }
1926 
1927 /***********************************************************************
1928  * hb_dvdnav_set_angle
1929  ***********************************************************************
1930  * Sets the angle to read
1931  **********************************************************************/
hb_dvdnav_set_angle(hb_dvd_t * e,int angle)1932 static void hb_dvdnav_set_angle( hb_dvd_t * e, int angle )
1933 {
1934     hb_dvdnav_t * d = &(e->dvdnav);
1935 
1936     if (dvdnav_angle_change( d->dvdnav, angle) != DVDNAV_STATUS_OK)
1937     {
1938         hb_log("dvdnav_angle_change %s", dvdnav_err_to_string(d->dvdnav));
1939     }
1940 }
1941 
1942 /***********************************************************************
1943  * FindChapterIndex
1944  ***********************************************************************
1945  * Assumes pgc and cell_cur are correctly set, and sets cell_next to the
1946  * cell to be read when we will be done with cell_cur.
1947  **********************************************************************/
FindChapterIndex(hb_list_t * list,int pgcn,int pgn)1948 static int FindChapterIndex( hb_list_t * list, int pgcn, int pgn )
1949 {
1950     int count, ii;
1951     hb_dvd_chapter_t * chapter;
1952 
1953     count = hb_list_count( list );
1954     for (ii = 0; ii < count; ii++)
1955     {
1956         chapter = hb_list_item( list, ii );
1957         if (chapter->pgcn == pgcn && chapter->pgn == pgn)
1958             return chapter->index;
1959     }
1960     return 0;
1961 }
1962 
1963 /***********************************************************************
1964  * FindNextCell
1965  ***********************************************************************
1966  * Assumes pgc and cell_cur are correctly set, and sets cell_next to the
1967  * cell to be read when we will be done with cell_cur.
1968  **********************************************************************/
FindNextCell(pgc_t * pgc,int cell_cur)1969 static int FindNextCell( pgc_t *pgc, int cell_cur )
1970 {
1971     int i = 0;
1972     int cell_next;
1973 
1974     if( pgc->cell_playback[cell_cur].block_type ==
1975             BLOCK_TYPE_ANGLE_BLOCK )
1976     {
1977 
1978         while( pgc->cell_playback[cell_cur+i].block_mode !=
1979                    BLOCK_MODE_LAST_CELL )
1980         {
1981              i++;
1982         }
1983         cell_next = cell_cur + i + 1;
1984         hb_log( "dvd: Skipping multi-angle cells %d-%d",
1985                 cell_cur,
1986                 cell_next - 1 );
1987     }
1988     else
1989     {
1990         cell_next = cell_cur + 1;
1991     }
1992     return cell_next;
1993 }
1994 
1995 /***********************************************************************
1996  * NextPgcn
1997  ***********************************************************************
1998  * Assumes pgc and cell_cur are correctly set, and sets cell_next to the
1999  * cell to be read when we will be done with cell_cur.
2000  * Since pg chains can be circularly linked (either from a read error or
2001  * deliberately) pgcn_map tracks program chains we've already seen.
2002  **********************************************************************/
NextPgcn(ifo_handle_t * ifo,int pgcn,uint32_t pgcn_map[MAX_PGCN/32])2003 static int NextPgcn( ifo_handle_t *ifo, int pgcn, uint32_t pgcn_map[MAX_PGCN/32] )
2004 {
2005     int next_pgcn;
2006     pgc_t *pgc;
2007 
2008     pgcn_map[pgcn >> 5] |= (1 << (pgcn & 31));
2009 
2010     pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc;
2011     next_pgcn = pgc->next_pgc_nr;
2012     if ( next_pgcn < 1 || next_pgcn >= MAX_PGCN || next_pgcn > ifo->vts_pgcit->nr_of_pgci_srp )
2013         return 0;
2014 
2015     return pgcn_map[next_pgcn >> 5] & (1 << (next_pgcn & 31))? 0 : next_pgcn;
2016 }
2017 
2018 /***********************************************************************
2019  * PgcWalkInit
2020  ***********************************************************************
2021  * Pgc links can loop. I track which have been visited in a bit vector
2022  * Initialize the bit vector to empty.
2023  **********************************************************************/
PgcWalkInit(uint32_t pgcn_map[MAX_PGCN/32])2024 static void PgcWalkInit( uint32_t pgcn_map[MAX_PGCN/32] )
2025 {
2026     memset(pgcn_map, 0, sizeof(uint32_t) * MAX_PGCN/32);
2027 }
2028 
2029 /***********************************************************************
2030  * dvdtime2msec
2031  ***********************************************************************
2032  * From lsdvd
2033  **********************************************************************/
dvdtime2msec(dvd_time_t * dt)2034 static int dvdtime2msec(dvd_time_t * dt)
2035 {
2036     double frames_per_s[4] = {-1.0, 25.00, -1.0, 29.97};
2037     double fps = frames_per_s[(dt->frame_u & 0xc0) >> 6];
2038     long   ms;
2039     ms  = (((dt->hour &   0xf0) >> 3) * 5 + (dt->hour   & 0x0f)) * 3600000;
2040     ms += (((dt->minute & 0xf0) >> 3) * 5 + (dt->minute & 0x0f)) * 60000;
2041     ms += (((dt->second & 0xf0) >> 3) * 5 + (dt->second & 0x0f)) * 1000;
2042 
2043     if( fps > 0 )
2044     {
2045         ms += (((dt->frame_u & 0x30) >> 3) * 5 +
2046                 (dt->frame_u & 0x0f)) * 1000.0 / fps;
2047     }
2048 
2049     return ms;
2050 }
2051 
TitleOpenIfo(hb_dvdnav_t * d,int t)2052 static int TitleOpenIfo(hb_dvdnav_t * d, int t)
2053 {
2054     int                pgcn, pgn, pgcn_end, i, c;
2055     pgc_t            * pgc;
2056     int                cell_cur;
2057     hb_dvd_chapter_t * dvd_chapter;
2058     uint64_t           duration;
2059 
2060     if (d->title == t && d->ifo != NULL)
2061     {
2062         // Already opened
2063         return 0;
2064     }
2065 
2066     // Close previous if open
2067     TitleCloseIfo(d);
2068 
2069     /* VTS which our title is in */
2070     d->vts = d->vmg->tt_srpt->title[t-1].title_set_nr;
2071 
2072     if (!d->vts)
2073     {
2074         /* A VTS of 0 means the title wasn't found in the title set */
2075         hb_log("Invalid VTS (title set) number: %i", d->vts);
2076         goto fail;
2077     }
2078 
2079     if(!(d->ifo = ifoOpen(d->reader, d->vts)))
2080     {
2081         hb_log( "ifoOpen failed" );
2082         goto fail;
2083     }
2084 
2085     int title_ttn = d->vmg->tt_srpt->title[t-1].vts_ttn;
2086     if ( title_ttn < 1 || title_ttn > d->ifo->vts_ptt_srpt->nr_of_srpts )
2087     {
2088         hb_log( "invalid VTS PTT offset %d for title %d, skipping", title_ttn, t );
2089         goto fail;
2090     }
2091 
2092     d->duration = 0LL;
2093     d->pgcn     = -1;
2094     d->pgn      = 1;
2095     for (i = 0; i < d->ifo->vts_ptt_srpt->title[title_ttn-1].nr_of_ptts; i++)
2096     {
2097         int blocks = 0;
2098 
2099         duration = PttDuration(d->ifo, title_ttn, i+1, &blocks, &pgcn_end);
2100         pgcn     = d->ifo->vts_ptt_srpt->title[title_ttn-1].ptt[i].pgcn;
2101         pgn      = d->ifo->vts_ptt_srpt->title[title_ttn-1].ptt[i].pgn;
2102         if (duration > d->duration)
2103         {
2104             d->pgcn              = pgcn;
2105             d->pgn               = pgn;
2106             d->duration          = duration;
2107             d->title_block_count = blocks;
2108         }
2109         else if (pgcn == d->pgcn && pgn < d->pgn)
2110         {
2111             d->pgn               = pgn;
2112             d->title_block_count = blocks;
2113         }
2114     }
2115 
2116     /* Check pgc */
2117     if ( d->pgcn < 1 || d->pgcn > d->ifo->vts_pgcit->nr_of_pgci_srp || d->pgcn >= MAX_PGCN)
2118     {
2119         hb_log( "invalid PGC ID %d for title %d, skipping", d->pgcn, t );
2120         goto fail;
2121     }
2122 
2123     /* Chapters */
2124     d->list_dvd_chapter = hb_list_init();
2125 
2126     uint32_t pgcn_map[MAX_PGCN/32];
2127     PgcWalkInit( pgcn_map );
2128     pgcn = d->pgcn;
2129     pgn  = d->pgn;
2130     c = 0;
2131     do
2132     {
2133         pgc = d->ifo->vts_pgcit->pgci_srp[pgcn-1].pgc;
2134 
2135         for (i = pgn; i <= pgc->nr_of_programs; i++)
2136         {
2137             int cell_start, cell_end;
2138 
2139             dvd_chapter = calloc(sizeof(hb_dvd_chapter_t), 1);
2140 
2141             dvd_chapter->pgcn  = pgcn;
2142             dvd_chapter->pgn   = i;
2143             dvd_chapter->index = c + 1;
2144 
2145             cell_start  = pgc->program_map[i-1] - 1;
2146             dvd_chapter->block_start = pgc->cell_playback[cell_start].first_sector;
2147 
2148             // if there are no more programs in this pgc, the end cell is the
2149             // last cell. Otherwise it's the cell before the start cell of the
2150             // next program.
2151             if (i == pgc->nr_of_programs)
2152             {
2153                 cell_end = pgc->nr_of_cells - 1;
2154             }
2155             else
2156             {
2157                 cell_end = pgc->program_map[i] - 2;
2158             }
2159             dvd_chapter->block_end = pgc->cell_playback[cell_end].last_sector;
2160 
2161             /* duration */
2162             dvd_chapter->duration = 0;
2163 
2164             cell_cur = cell_start;
2165             while( cell_cur <= cell_end )
2166             {
2167 #define cp pgc->cell_playback[cell_cur]
2168                 dvd_chapter->duration += 90LL * dvdtime2msec(&cp.playback_time);
2169 #undef cp
2170                 cell_cur = FindNextCell( pgc, cell_cur );
2171             }
2172             hb_list_add(d->list_dvd_chapter, dvd_chapter);
2173             c++;
2174         }
2175         pgn = 1;
2176     } while ((pgcn = NextPgcn(d->ifo, pgcn, pgcn_map)) != 0);
2177 
2178     d->title = t;
2179     return 1;
2180 
2181 fail:
2182     TitleCloseIfo(d);
2183     return 0;
2184 }
2185 
TitleCloseIfo(hb_dvdnav_t * d)2186 static void TitleCloseIfo(hb_dvdnav_t * d)
2187 {
2188     hb_dvd_chapter_t * chapter;
2189     while ((chapter = hb_list_item(d->list_dvd_chapter, 0)))
2190     {
2191         hb_list_rem(d->list_dvd_chapter, chapter );
2192         free(chapter);
2193     }
2194     hb_list_close(&d->list_dvd_chapter);
2195 
2196     if (d->ifo)
2197     {
2198         ifoClose(d->ifo);
2199     }
2200     d->ifo   = NULL;
2201     d->title = 0;
2202     d->vts   = 0;
2203     d->pgcn  = 0;
2204     d->pgn   = 0;
2205 }