/* $Id: dvd.c,v 1.12 2005/11/25 15:05:25 titer Exp $
This file is part of the HandBrake source code.
- Homepage: <http://handbrake.m0k.org/>.
+ Homepage: <http://handbrake.fr/>.
It may be used under the terms of the GNU General Public License. */
#include "hb.h"
#include "lang.h"
+#include "dvd.h"
#include "dvdread/ifo_read.h"
+#include "dvdread/ifo_print.h"
#include "dvdread/nav_read.h"
-struct hb_dvd_s
+static hb_dvd_t * hb_dvdread_init( char * path );
+static void hb_dvdread_close( hb_dvd_t ** _d );
+static char * hb_dvdread_name( char * path );
+static int hb_dvdread_title_count( hb_dvd_t * d );
+static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * d, int t );
+static int hb_dvdread_start( hb_dvd_t * d, hb_title_t *title, int chapter );
+static void hb_dvdread_stop( hb_dvd_t * d );
+static int hb_dvdread_seek( hb_dvd_t * d, float f );
+static int hb_dvdread_read( hb_dvd_t * d, hb_buffer_t * b );
+static int hb_dvdread_chapter( hb_dvd_t * d );
+static int hb_dvdread_angle_count( hb_dvd_t * d );
+static void hb_dvdread_set_angle( hb_dvd_t * d, int angle );
+
+hb_dvd_func_t hb_dvdread_func =
{
- char * path;
-
- dvd_reader_t * reader;
- ifo_handle_t * vmg;
-
- int vts;
- int ttn;
- ifo_handle_t * ifo;
- dvd_file_t * file;
-
- pgc_t * pgc;
- int cell_start;
- int cell_end;
- int title_start;
- int title_end;
- int title_block_count;
- int cell_cur;
- int cell_next;
- int cell_overlap;
- int block;
- int pack_len;
- int next_vobu;
- int in_cell;
- int in_sync;
- uint16_t cur_vob_id;
- uint8_t cur_cell_id;
+ hb_dvdread_init,
+ hb_dvdread_close,
+ hb_dvdread_name,
+ hb_dvdread_title_count,
+ hb_dvdread_title_scan,
+ hb_dvdread_start,
+ hb_dvdread_stop,
+ hb_dvdread_seek,
+ hb_dvdread_read,
+ hb_dvdread_chapter,
+ hb_dvdread_angle_count,
+ hb_dvdread_set_angle
};
+static hb_dvd_func_t *dvd_methods = &hb_dvdread_func;
+
/***********************************************************************
* Local prototypes
**********************************************************************/
-static void FindNextCell( hb_dvd_t * );
+static void FindNextCell( hb_dvdread_t * );
static int dvdtime2msec( dvd_time_t * );
+static int hb_dvdread_is_break( hb_dvdread_t * d );
-char * hb_dvd_name( char * path )
+hb_dvd_func_t * hb_dvdread_methods( void )
+{
+ return &hb_dvdread_func;
+}
+
+static char * hb_dvdread_name( char * path )
{
static char name[1024];
unsigned char unused[1024];
}
/***********************************************************************
- * hb_dvd_init
+ * hb_dvdread_init
***********************************************************************
*
**********************************************************************/
-hb_dvd_t * hb_dvd_init( char * path )
+hb_dvd_t * hb_dvdread_init( char * path )
{
- hb_dvd_t * d;
+ hb_dvd_t * e;
+ hb_dvdread_t * d;
+ int region_mask;
- d = calloc( sizeof( hb_dvd_t ), 1 );
+ e = calloc( sizeof( hb_dvd_t ), 1 );
+ d = &(e->dvdread);
+
+ /* Log DVD drive region code */
+ if ( hb_dvd_region( path, ®ion_mask ) == 0 )
+ {
+ hb_log( "dvd: Region mask 0x%02x", region_mask );
+ if ( region_mask == 0xFF )
+ {
+ hb_log( "dvd: Warning, DVD device has no region set" );
+ }
+ }
/* Open device */
if( !( d->reader = DVDOpen( path ) ) )
{
- hb_error( "dvd: DVDOpen failed (%s)", path );
+ /*
+ * Not an error, may be a stream - which we'll try in a moment.
+ */
+ hb_log( "dvd: not a dvd - trying as a stream/file instead" );
goto fail;
}
d->path = strdup( path );
- return d;
+ return e;
fail:
if( d->vmg ) ifoClose( d->vmg );
}
/***********************************************************************
- * hb_dvd_title_count
+ * hb_dvdread_title_count
**********************************************************************/
-int hb_dvd_title_count( hb_dvd_t * d )
+static int hb_dvdread_title_count( hb_dvd_t * e )
{
+ hb_dvdread_t *d = &(e->dvdread);
return d->vmg->tt_srpt->nr_of_srpts;
}
/***********************************************************************
- * hb_dvd_title_scan
+ * hb_dvdread_title_scan
**********************************************************************/
-hb_title_t * hb_dvd_title_scan( hb_dvd_t * d, int t )
+static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t )
{
+ hb_dvdread_t *d = &(e->dvdread);
hb_title_t * title;
ifo_handle_t * vts = NULL;
int pgc_id, pgn, i;
hb_log( "scan: scanning title %d", t );
title = hb_title_init( d->path, t );
+ title->type = HB_DVD_TYPE;
+
if( DVDUDFVolumeInfo( d->reader, title->name, sizeof( title->name ),
unused, sizeof( unused ) ) )
{
goto fail;
}
+ /* ignore titles with bogus cell addresses so we don't abort later
+ * in libdvdread. */
+ for ( i = 0; i < vts->vts_c_adt->nr_of_vobs; ++i)
+ {
+ if( (vts->vts_c_adt->cell_adr_table[i].start_sector & 0xffffff ) ==
+ 0xffffff )
+ {
+ hb_error( "scan: cell_adr_table[%d].start_sector invalid (0x%x) "
+ "- skipping title", i,
+ vts->vts_c_adt->cell_adr_table[i].start_sector );
+ goto fail;
+ }
+ if( (vts->vts_c_adt->cell_adr_table[i].last_sector & 0xffffff ) ==
+ 0xffffff )
+ {
+ hb_error( "scan: cell_adr_table[%d].last_sector invalid (0x%x) "
+ "- skipping title", i,
+ vts->vts_c_adt->cell_adr_table[i].last_sector );
+ goto fail;
+ }
+ if( vts->vts_c_adt->cell_adr_table[i].start_sector >=
+ vts->vts_c_adt->cell_adr_table[i].last_sector )
+ {
+ hb_error( "scan: cell_adr_table[%d].start_sector (0x%x) "
+ "is not before last_sector (0x%x) - skipping title", i,
+ vts->vts_c_adt->cell_adr_table[i].start_sector,
+ vts->vts_c_adt->cell_adr_table[i].last_sector );
+ goto fail;
+ }
+ }
+
+ if( global_verbosity_level == 3 )
+ {
+ ifo_print( d->reader, title->vts );
+ }
+
/* Position of the title in the VTS */
title->ttn = d->vmg->tt_srpt->title[t-1].vts_ttn;
+ if ( title->ttn < 1 || title->ttn > vts->vts_ptt_srpt->nr_of_srpts )
+ {
+ hb_error( "invalid VTS PTT offset %d for title %d, skipping", title->ttn, t );
+ goto fail;
+ }
/* Get pgc */
pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgcn;
+ if ( pgc_id < 1 || pgc_id > vts->vts_pgcit->nr_of_pgci_srp )
+ {
+ hb_error( "invalid PGC ID %d for title %d, skipping", pgc_id, t );
+ goto fail;
+ }
pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgn;
d->pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc;
- hb_log("pgc_id: %d, pgn: %d: pgc: 0x%x", pgc_id, pgn, d->pgc);
+ hb_log("pgc_id: %d, pgn: %d: pgc: %p", pgc_id, pgn, d->pgc);
if( !d->pgc )
{
title->hours = title->duration / 90000 / 3600;
title->minutes = ( ( title->duration / 90000 ) % 3600 ) / 60;
title->seconds = ( title->duration / 90000 ) % 60;
- hb_log( "scan: duration is %02d:%02d:%02d (%lld ms)",
+ hb_log( "scan: duration is %02d:%02d:%02d (%"PRId64" ms)",
title->hours, title->minutes, title->seconds,
title->duration / 90 );
int audio_format, lang_code, audio_control,
position, j;
iso639_lang_t * lang;
+ int lang_extension = 0;
hb_log( "scan: checking audio %d", i + 1 );
audio_format = vts->vtsi_mat->vts_audio_attr[i].audio_format;
lang_code = vts->vtsi_mat->vts_audio_attr[i].lang_code;
+ lang_extension = vts->vtsi_mat->vts_audio_attr[i].code_extension;
audio_control =
vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->audio_control[i];
continue;
}
+ audio->config.lang.type = lang_extension;
+
lang = lang_for_code( vts->vtsi_mat->vts_audio_attr[i].lang_code );
snprintf( audio->config.lang.description, sizeof( audio->config.lang.description ), "%s (%s)",
snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ), "%s",
lang->iso639_2);
- hb_log( "scan: id=%x, lang=%s, 3cc=%s", audio->id,
- audio->config.lang.description, audio->config.lang.iso639_2 );
+ switch( lang_extension )
+ {
+ case 0:
+ case 1:
+ break;
+ case 2:
+ strcat( audio->config.lang.description, " (Visually Impaired)" );
+ break;
+ case 3:
+ strcat( audio->config.lang.description, " (Director's Commentary 1)" );
+ break;
+ case 4:
+ strcat( audio->config.lang.description, " (Director's Commentary 2)" );
+ break;
+ default:
+ break;
+ }
+
+ hb_log( "scan: id=%x, lang=%s, 3cc=%s ext=%i", audio->id,
+ audio->config.lang.description, audio->config.lang.iso639_2,
+ lang_extension );
audio->config.in.track = i;
hb_list_add( title->list_audio, audio );
}
- if( !hb_list_count( title->list_audio ) )
- {
- hb_log( "scan: ignoring title (no audio track)" );
- goto fail;
- }
-
memcpy( title->palette,
vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->palette,
16 * sizeof( uint32_t ) );
int spu_control;
int position;
iso639_lang_t * lang;
+ int lang_extension = 0;
hb_log( "scan: checking subtitle %d", i + 1 );
position = ( spu_control >> 24 ) & 0x7F;
}
+ lang_extension = vts->vtsi_mat->vts_subp_attr[i].code_extension;
+
lang = lang_for_code( vts->vtsi_mat->vts_subp_attr[i].lang_code );
subtitle = calloc( sizeof( hb_subtitle_t ), 1 );
+ subtitle->track = i+1;
subtitle->id = ( ( 0x20 + position ) << 8 ) | 0xbd;
snprintf( subtitle->lang, sizeof( subtitle->lang ), "%s",
strlen(lang->native_name) ? lang->native_name : lang->eng_name);
- snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s",
- lang->iso639_2);
+ snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s",
+ lang->iso639_2);
+ subtitle->format = PICTURESUB;
+ subtitle->source = VOBSUB;
+ subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB.
+
+ subtitle->type = lang_extension;
+
+ switch( lang_extension )
+ {
+ case 0:
+ break;
+ case 1:
+ break;
+ case 2:
+ strcat( subtitle->lang, " (Caption with bigger size character)");
+ break;
+ case 3:
+ strcat( subtitle->lang, " (Caption for Children)");
+ break;
+ case 4:
+ break;
+ case 5:
+ strcat( subtitle->lang, " (Closed Caption)");
+ break;
+ case 6:
+ strcat( subtitle->lang, " (Closed Caption with bigger size character)");
+ break;
+ case 7:
+ strcat( subtitle->lang, " (Closed Caption for Children)");
+ break;
+ case 8:
+ break;
+ case 9:
+ strcat( subtitle->lang, " (Forced Caption)");
+ break;
+ case 10:
+ break;
+ case 11:
+ break;
+ case 12:
+ break;
+ case 13:
+ strcat( subtitle->lang, " (Director's Commentary)");
+ break;
+ case 14:
+ strcat( subtitle->lang, " (Director's Commentary with bigger size character)");
+ break;
+ case 15:
+ strcat( subtitle->lang, " (Director's Commentary for Children)");
+ default:
+ break;
+ }
hb_log( "scan: id=%x, lang=%s, 3cc=%s", subtitle->id,
subtitle->lang, subtitle->iso639_2 );
for( i = 0, c = 1;
i < vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts; i++ )
{
- int pgc_id_next, pgn_next;
- pgc_t * pgc_next;
-
chapter = calloc( sizeof( hb_chapter_t ), 1 );
/* remember the on-disc chapter number */
chapter->index = i + 1;
chapter->block_start =
d->pgc->cell_playback[chapter->cell_start].first_sector;
- /* End cell */
- if( i != vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts - 1 )
+ // if there are no more programs in this pgc, the end cell is the
+ // last cell. Otherwise it's the cell before the start cell of the
+ // next program.
+ if ( pgn == d->pgc->nr_of_programs )
{
- /* The cell before the starting cell of the next chapter,
- or... */
- pgc_id_next = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i+1].pgcn;
- pgn_next = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i+1].pgn;
- pgc_next = vts->vts_pgcit->pgci_srp[pgc_id_next-1].pgc;
- chapter->cell_end = pgc_next->program_map[pgn_next-1] - 2;
- if( chapter->cell_end < 0 )
- {
- /* Huh? */
- free( chapter );
- continue;
- }
+ chapter->cell_end = d->pgc->nr_of_cells - 1;
}
else
{
- /* ... the last cell of the title */
- chapter->cell_end = title->cell_end;
+ chapter->cell_end = d->pgc->program_map[pgn] - 2;;
}
- chapter->block_end =
- d->pgc->cell_playback[chapter->cell_end].last_sector;
+ chapter->block_end = d->pgc->cell_playback[chapter->cell_end].last_sector;
/* Block count, duration */
- pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgcn;
- pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgn;
- d->pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc;
chapter->block_count = 0;
chapter->duration = 0;
chapter->minutes = ( seconds % 3600 ) / 60;
chapter->seconds = seconds % 60;
- hb_log( "scan: chap %d c=%d->%d, b=%d->%d (%d), %lld ms",
+ hb_log( "scan: chap %d c=%d->%d, b=%d->%d (%d), %"PRId64" ms",
chapter->index, chapter->cell_start, chapter->cell_end,
chapter->block_start, chapter->block_end,
chapter->block_count, chapter->duration / 90 );
switch( vts->vtsi_mat->vts_video_attr.display_aspect_ratio )
{
case 0:
- title->aspect = HB_ASPECT_BASE * 4 / 3;
+ title->container_aspect = 4. / 3.;
break;
case 3:
- title->aspect = HB_ASPECT_BASE * 16 / 9;
+ title->container_aspect = 16. / 9.;
break;
default:
hb_log( "scan: unknown aspect" );
goto fail;
}
- hb_log( "scan: aspect = %d", title->aspect );
+ hb_log( "scan: aspect = %g", title->aspect );
/* This title is ok so far */
goto cleanup;
}
/***********************************************************************
- * hb_dvd_start
+ * hb_dvdread_start
***********************************************************************
* Title and chapter start at 1
**********************************************************************/
-int hb_dvd_start( hb_dvd_t * d, int title, int chapter )
+static int hb_dvdread_start( hb_dvd_t * e, hb_title_t *title, int chapter )
{
+ hb_dvdread_t *d = &(e->dvdread);
int pgc_id, pgn;
int i;
+ int t = title->index;
/* Open the IFO and the VOBs for this title */
- d->vts = d->vmg->tt_srpt->title[title-1].title_set_nr;
- d->ttn = d->vmg->tt_srpt->title[title-1].vts_ttn;
+ d->vts = d->vmg->tt_srpt->title[t-1].title_set_nr;
+ d->ttn = d->vmg->tt_srpt->title[t-1].vts_ttn;
if( !( d->ifo = ifoOpen( d->reader, d->vts ) ) )
{
hb_error( "dvd: ifoOpen failed for VTS %d", d->vts );
}
/***********************************************************************
- * hb_dvd_stop
+ * hb_dvdread_stop
***********************************************************************
*
**********************************************************************/
-void hb_dvd_stop( hb_dvd_t * d )
+static void hb_dvdread_stop( hb_dvd_t * e )
{
+ hb_dvdread_t *d = &(e->dvdread);
if( d->ifo )
{
ifoClose( d->ifo );
}
/***********************************************************************
- * hb_dvd_seek
+ * hb_dvdread_seek
***********************************************************************
*
**********************************************************************/
-int hb_dvd_seek( hb_dvd_t * d, float f )
+static int hb_dvdread_seek( hb_dvd_t * e, float f )
{
+ hb_dvdread_t *d = &(e->dvdread);
int count, sizeCell;
int i;
d->cur_cell_id = 0;
FindNextCell( d );
- /* Now let hb_dvd_read find the next VOBU */
+ /* Now let hb_dvdread_read find the next VOBU */
d->next_vobu = d->pgc->cell_playback[i].first_sector + count;
d->pack_len = 0;
break;
/***********************************************************************
- * hb_dvd_read
+ * hb_dvdread_read
***********************************************************************
*
**********************************************************************/
-int hb_dvd_read( hb_dvd_t * d, hb_buffer_t * b )
+static int hb_dvdread_read( hb_dvd_t * e, hb_buffer_t * b )
{
+ hb_dvdread_t *d = &(e->dvdread);
top:
if( !d->pack_len )
{
}
else
{
- hb_log( "dvd: Beginning of Cell (%d) at block %d", d->cell_cur,
- d->block );
+ if ( d->block != d->pgc->cell_playback[d->cell_cur].first_sector )
+ {
+ hb_log( "dvd: beginning of cell %d at block %d", d->cell_cur,
+ d->block );
+ }
if( d->in_cell )
{
- hb_error( "dvd: Assuming missed End of Cell (%d)", d->cell_cur );
+ hb_error( "dvd: assuming missed end of cell %d", d->cell_cur );
d->cell_cur = d->cell_next;
d->next_vobu = d->pgc->cell_playback[d->cell_cur].first_sector;
FindNextCell( d );
if( d->cell_overlap )
{
- b->new_chap = hb_dvd_is_break( d );
+ b->new_chap = hb_dvdread_is_break( d );
d->cell_overlap = 0;
}
}
if( ( dsi_pack.vobu_sri.next_vobu & (1 << 31 ) ) == 0 ||
( dsi_pack.vobu_sri.next_vobu & 0x3fffffff ) == 0x3fffffff )
{
- hb_log( "dvd: End of Cell (%d) at block %d", d->cell_cur,
- d->block );
+ if ( d->block <= d->pgc->cell_playback[d->cell_cur].first_sector ||
+ d->block > d->pgc->cell_playback[d->cell_cur].last_sector )
+ {
+ hb_log( "dvd: end of cell %d at block %d", d->cell_cur,
+ d->block );
+ }
d->cell_cur = d->cell_next;
d->in_cell = 0;
d->next_vobu = d->pgc->cell_playback[d->cell_cur].first_sector;
}
/***********************************************************************
- * hb_dvd_chapter
+ * hb_dvdread_chapter
***********************************************************************
* Returns in which chapter the next block to be read is.
* Chapter numbers start at 1.
**********************************************************************/
-int hb_dvd_chapter( hb_dvd_t * d )
+static int hb_dvdread_chapter( hb_dvd_t * e )
{
+ hb_dvdread_t *d = &(e->dvdread);
int i;
int pgc_id, pgn;
int nr_of_ptts = d->ifo->vts_ptt_srpt->title[d->ttn-1].nr_of_ptts;
}
/***********************************************************************
- * hb_dvd_is_break
+ * hb_dvdread_is_break
***********************************************************************
* Returns chapter number if the current block is a new chapter start
**********************************************************************/
-int hb_dvd_is_break( hb_dvd_t * d )
+static int hb_dvdread_is_break( hb_dvdread_t * d )
{
int i;
int pgc_id, pgn;
- int nr_of_ptts = d->ifo->vts_ptt_srpt->title[d->ttn-1].nr_of_ptts;
+ int nr_of_ptts = d->ifo->vts_ptt_srpt->title[d->ttn-1].nr_of_ptts;
pgc_t * pgc;
int cell;
}
/***********************************************************************
- * hb_dvd_close
+ * hb_dvdread_close
***********************************************************************
* Closes and frees everything
**********************************************************************/
-void hb_dvd_close( hb_dvd_t ** _d )
+static void hb_dvdread_close( hb_dvd_t ** _d )
{
- hb_dvd_t * d = *_d;
+ hb_dvdread_t * d = &((*_d)->dvdread);
if( d->vmg )
{
}
/***********************************************************************
+ * hb_dvdread_angle_count
+ ***********************************************************************
+ * Returns the number of angles supported. We do not support angles
+ * with dvdread
+ **********************************************************************/
+static int hb_dvdread_angle_count( hb_dvd_t * d )
+{
+ return 1;
+}
+
+/***********************************************************************
+ * hb_dvdread_set_angle
+ ***********************************************************************
+ * Sets the angle to read. Not supported with dvdread
+ **********************************************************************/
+static void hb_dvdread_set_angle( hb_dvd_t * d, int angle )
+{
+}
+
+/***********************************************************************
* FindNextCell
***********************************************************************
* Assumes pgc and cell_cur are correctly set, and sets cell_next to the
* cell to be read when we will be done with cell_cur.
**********************************************************************/
-static void FindNextCell( hb_dvd_t * d )
+static void FindNextCell( hb_dvdread_t * d )
{
int i = 0;
return ms;
}
+
+char * hb_dvd_name( char * path )
+{
+ return dvd_methods->name(path);
+}
+
+hb_dvd_t * hb_dvd_init( char * path )
+{
+ return dvd_methods->init(path);
+}
+
+int hb_dvd_title_count( hb_dvd_t * d )
+{
+ return dvd_methods->title_count(d);
+}
+
+hb_title_t * hb_dvd_title_scan( hb_dvd_t * d, int t )
+{
+ return dvd_methods->title_scan(d, t);
+}
+
+int hb_dvd_start( hb_dvd_t * d, hb_title_t *title, int chapter )
+{
+ return dvd_methods->start(d, title, chapter);
+}
+
+void hb_dvd_stop( hb_dvd_t * d )
+{
+ dvd_methods->stop(d);
+}
+
+int hb_dvd_seek( hb_dvd_t * d, float f )
+{
+ return dvd_methods->seek(d, f);
+}
+
+int hb_dvd_read( hb_dvd_t * d, hb_buffer_t * b )
+{
+ return dvd_methods->read(d, b);
+}
+
+int hb_dvd_chapter( hb_dvd_t * d )
+{
+ return dvd_methods->chapter(d);
+}
+
+void hb_dvd_close( hb_dvd_t ** _d )
+{
+ dvd_methods->close(_d);
+}
+
+int hb_dvd_angle_count( hb_dvd_t * d )
+{
+ return dvd_methods->angle_count(d);
+}
+
+void hb_dvd_set_angle( hb_dvd_t * d, int angle )
+{
+ dvd_methods->set_angle(d, angle);
+}
+
+// hb_dvd_set_dvdnav must only be called when no dvd source is open
+// it rips the rug out from under things so be careful
+void hb_dvd_set_dvdnav( int enable )
+{
+ if (enable)
+ dvd_methods = hb_dvdnav_methods();
+ else
+ dvd_methods = hb_dvdread_methods();
+}
+