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, ®ion_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, ¤t, &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 }