/* * Copyright (C) 2001 Billy Biggs . * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #if defined(HAVE_INTTYPES_H) #include #elif defined(HAVE_STDINT_H) #include #endif #include #include #include #include #include /** * Returns true if the pack is a NAV pack. This check is clearly insufficient, * and sometimes we incorrectly think that valid other packs are NAV packs. I * need to make this stronger. */ int is_nav_pack( unsigned char *buffer ) { return ( buffer[ 41 ] == 0xbf && buffer[ 1027 ] == 0xbf ); } static unsigned int bcd_rate(unsigned int f) { f >>= 6; if(f == 0x3) return 30; else if(f == 0x1) return 25; else return f; } static dvd_time_t fix_time (dvd_time_t t) { unsigned char r = t.frame_u & 0xc0; t.frame_u = t.frame_u & 0x3f; t.hour = 10 *((t.hour >> 4) & 0xf) + (t.hour & 0xf); t.minute = 10 *((t.minute >> 4) & 0xf) + (t.minute & 0xf); t.second = 10 *((t.second >> 4) & 0xf) + (t.second & 0xf); t.frame_u = 10 *((t.frame_u >>4) & 0xf) + (t.frame_u & 0xf); t.frame_u ^= r; return t; } static dvd_time_t add_time (dvd_time_t t1, dvd_time_t t2) { dvd_time_t out; unsigned int r1 = bcd_rate(t1.frame_u); unsigned int f1 = t1.frame_u & 0x3f; unsigned int r2 = bcd_rate(t2.frame_u); unsigned int f2 = t2.frame_u & 0x3f; if( (!r1) && (!f1) ) { r1 = r2; t1.frame_u = t2.frame_u; } if( (!r2) && (!f2) ) { r2 = r1; t2.frame_u = t1.frame_u; } assert(r1 == r2); out.frame_u = f1 + f2; out.second = (t1.second + t2.second); out.minute = (t1.minute + t2.minute); out.hour = (t1.hour + t2.hour); if(out.frame_u >= r1) { out.second ++; out.frame_u = out.frame_u % r1; } out.frame_u ^= (t1.frame_u & 0xc0); if(out.second >= 60) { out.minute += (uint8_t) (out.second / 60); out.second = out.second % 60; } if(out.minute >= 60) { out.hour += (uint8_t) (out.minute / 60); out.minute = out.minute % 60; } return out; } int main( int argc, char **argv ) { int titleid, chapid = 0, pgc_id, len, start_cell = 0, cur_cell; unsigned int cur_pack; int angle = 0, ttn, pgn, next_cell; unsigned char data[DVD_VIDEO_LB_LEN ]; dvd_reader_t *dvd; dvd_file_t *title; ifo_handle_t *vmg_file; tt_srpt_t *tt_srpt; ifo_handle_t *vts_file; vts_ptt_srpt_t *vts_ptt_srpt; pgc_t *cur_pgc; dvd_time_t total_time; dvd_time_t cell_time; int i; int next_pgn; int next_vobu; int cur_output_size; char *filename; int changefrom = 2; int changeto = 3; char time_s[20]; char *change_s; int audio_offset = 0; int audio_frame_size = 0; /** * Usage. */ if( argc < 3 ) { fprintf( stderr, "Usage: %s " "[<start chapter>] [ <cell offset> ] [ <angle> ]\n\n" "Title Number [1-X]: Movie Title on Disc.\n" "Start Chapter [1-X]: Chapter to start at.\n" "Start Cell [1-X]: Chapter to start at.\n" "Angle Number [1-X]: Which angle to show.\n\n", argv[ 0 ], argv[ 0 ] ); return -1; } argv++; argc--; filename = *argv++; argc--; titleid = atoi( *argv++ ) - 1; argc--; if(argc) { chapid = atoi( *argv++ ) - 1; argc--; } if(argc) { start_cell = atoi( *argv++ ) - 1; argc--;} if(argc) { angle = atoi( *argv++ ) - 1; argc--; } dvd = DVDOpen( filename ); if( !dvd ) { fprintf( stderr, "Couldn't open DVD: %s\n", argv[ 1 ] ); return -1; } /** * Load the video manager to find out the information about the titles on * this disc. */ vmg_file = ifoOpen( dvd, 0 ); if( !vmg_file ) { fprintf( stderr, "Can't open VMG info.\n" ); DVDClose( dvd ); return -1; } tt_srpt = vmg_file->tt_srpt; /** * Make sure our title number is valid. */ if( titleid < 0 || titleid >= tt_srpt->nr_of_srpts ) { fprintf( stderr, "Invalid title %d.\n", titleid + 1 ); ifoClose( vmg_file ); DVDClose( dvd ); return -1; } /** * Make sure the chapter number is valid for this title. */ if( chapid < 0 || chapid >= tt_srpt->title[ titleid ].nr_of_ptts ) { fprintf( stderr, "Invalid chapter %d\n", chapid + 1 ); ifoClose( vmg_file ); DVDClose( dvd ); return -1; } /** * Make sure the angle number is valid for this title. */ if( angle < 0 || angle >= tt_srpt->title[ titleid ].nr_of_angles ) { fprintf( stderr, "Invalid angle %d\n", angle + 1 ); ifoClose( vmg_file ); DVDClose( dvd ); return -1; } /** * Load the VTS information for the title set our title is in. */ vts_file = ifoOpen( dvd, tt_srpt->title[ titleid ].title_set_nr ); if( !vts_file ) { fprintf( stderr, "Can't open the title %d info file.\n", tt_srpt->title[ titleid ].title_set_nr ); ifoClose( vmg_file ); DVDClose( dvd ); return -1; } /** * Determine which program chain we want to watch. This is based on the * chapter number. */ ttn = tt_srpt->title[ titleid ].vts_ttn; vts_ptt_srpt = vts_file->vts_ptt_srpt; pgc_id = vts_ptt_srpt->title[ ttn - 1 ].ptt[ chapid ].pgcn; pgn = vts_ptt_srpt->title[ ttn - 1 ].ptt[ chapid ].pgn; cur_pgc = vts_file->vts_pgcit->pgci_srp[ pgc_id - 1 ].pgc; start_cell += cur_pgc->program_map[ pgn - 1 ] - 1; next_pgn = pgn; total_time.hour = 0; total_time.minute = 0; total_time.second = 0; total_time.frame_u = 0; /** * We've got enough info, time to open the title set data. */ title = DVDOpenFile( dvd, tt_srpt->title[ titleid ].title_set_nr, DVD_READ_TITLE_VOBS ); if( !title ) { fprintf( stderr, "Can't open title VOBS (VTS_%02d_1.VOB).\n", tt_srpt->title[ titleid ].title_set_nr ); ifoClose( vts_file ); ifoClose( vmg_file ); DVDClose( dvd ); return -1; } for( cur_cell = 0; cur_cell < start_cell; cur_cell++) { total_time = add_time(total_time, fix_time(cur_pgc->cell_playback[ cur_cell ].playback_time)); } if(start_cell != 0) { printf("-%02u:%02u:%02u.%02u/%02u\n", total_time.hour, total_time.minute, total_time.second, total_time.frame_u & 0x3f, bcd_rate(total_time.frame_u)); } total_time.hour = 0; total_time.minute = 0; total_time.second = 0; total_time.frame_u = 0; /** * Playback by cell in this pgc, starting at the cell for our chapter. */ for( cur_cell = start_cell; cur_cell < cur_pgc->nr_of_cells; cur_cell=next_cell) { dvd_time_t total_cell_time = fix_time(cur_pgc->cell_playback[ cur_cell ].playback_time); int first = 1; /* Check if we're entering an angle block. */ if( cur_pgc->cell_playback[ cur_cell ].block_type == BLOCK_TYPE_ANGLE_BLOCK ) { int i; cur_cell += angle; for( i = 0;; ++i ) { if( cur_pgc->cell_playback[ cur_cell + i ].block_mode == BLOCK_MODE_LAST_CELL ) { next_cell = cur_cell + i + 1; break; } } } else { next_cell = cur_cell + 1; } printf("%02u:%02u:%02u.%02u/%02u", total_time.hour, total_time.minute, total_time.second, total_time.frame_u & 0x3f, bcd_rate(total_time.frame_u)); printf(" cell=%03d", cur_cell +1); if(cur_cell == cur_pgc->program_map[ next_pgn - 1 ] - 1) { printf (" chapter=%d", next_pgn); next_pgn++; } printf("\n"); for( cur_pack = cur_pgc->cell_playback[ cur_cell ].first_sector; cur_pack < cur_pgc->cell_playback[ cur_cell ].last_sector; cur_pack = next_vobu) { int len = DVDReadBlocks( title, (int) cur_pack, 1, data ); dsi_t dsi_pack; pci_t pci_pack; // navRead_PCI(&pci_pack, &(data[DSI_START_BYTE - 7 - PCI_BYTES + 1])); navRead_DSI( &dsi_pack, &(data[ DSI_START_BYTE ]) ); assert( cur_pack == dsi_pack.dsi_gi.nv_pck_lbn ); cur_output_size = dsi_pack.dsi_gi.vobu_ea; if( dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL ) { next_vobu = cur_pack + ( dsi_pack.vobu_sri.next_vobu & 0x7fffffff ); } else { next_vobu = cur_pack + cur_output_size + 1; } cell_time = add_time(total_time, fix_time(dsi_pack.dsi_gi.c_eltm)); printf("%02u:%02u:%02u.%02u/%02u vobu start_sector=%u end_sector=%u ilvu_end_sector=%u category=%u", cell_time.hour, cell_time.minute, cell_time.second, cell_time.frame_u & 0x3f, bcd_rate(cell_time.frame_u), cur_pack, cur_pack + dsi_pack.dsi_gi.vobu_ea, dsi_pack.sml_pbi.ilvu_ea ? cur_pack + dsi_pack.sml_pbi.ilvu_ea : 0, dsi_pack.sml_pbi.category); printf(" (%03d/%03d)\n", dsi_pack.dsi_gi.vobu_vob_idn, dsi_pack.dsi_gi.vobu_c_idn); // fprintf(stderr, "t %d %d %d\n", // pci_pack.pci_gi.vobu_s_ptm, // pci_pack.pci_gi.vobu_e_ptm, // pci_pack.pci_gi.vobu_se_e_ptm); if(dsi_pack.sml_pbi.vob_a[0].gap_len1 || dsi_pack.sml_pbi.vob_a[2].gap_len1) fprintf(stderr, "t %d %d %d %d\n", dsi_pack.sml_pbi.vob_a[0].stp_ptm1, dsi_pack.sml_pbi.vob_a[0].gap_len1, dsi_pack.sml_pbi.vob_a[2].stp_ptm1, dsi_pack.sml_pbi.vob_a[2].gap_len1); } total_time = add_time(total_time, total_cell_time); } printf("%02u:%02u:%02u.%02u/%02u\n", total_time.hour, total_time.minute, total_time.second, total_time.frame_u & 0x3f, bcd_rate(total_time.frame_u)); ifoClose( vts_file ); ifoClose( vmg_file ); DVDCloseFile( title ); DVDClose( dvd ); return 0; }