/* dvdxchap -- utility for extracting chapter start times from IFO files Written by Moritz Bunkus Distributed under the GPL see the file COPYING for details or visit http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include void usage() { fprintf(stdout, "ogmtools v" VERSION "\n" "Usage: dvdxchap [options] DVD-SOURCE\n\n" " options:\n" " -t, --title num Use title 'num'. Default is 1.\n" " -c, --chapter start[-end] Chapter to start at (to end at). Default 1.\n" " -v, --verbose Increase verbosity\n" " -V, --version Show version information\n" " -h, --help Show this help\n"); } void display_chapters(char *source, int title, int start, int end, int verbose) { dvd_reader_t *dvd; ifo_handle_t *vmg; ifo_handle_t *vts; tt_srpt_t *tt_srpt; int ttn, pgn, pgc_id, start_cell, end_cell, i, j; vts_ptt_srpt_t *vts_ptt_srpt; pgc_t *cur_pgc; dvd_time_t *dt; double fps; long hour, minute, second, ms, overall_time, start_time, cur_time; dvd = DVDOpen(source); if (dvd == NULL) { fprintf(stderr, "(%s) Could not open source '%s'.\n", __FILE__, source); exit(1); } vmg = ifoOpen(dvd, 0); if (vmg == NULL) { fprintf(stderr, "(%s) Can't open VMG info.\n", __FILE__); DVDClose(dvd); exit(1); } tt_srpt = vmg->tt_srpt; if (verbose) fprintf(stderr, "(%s) This DVD contains %d titles.\n", __FILE__, tt_srpt->nr_of_srpts); if (title > tt_srpt->nr_of_srpts) { fprintf(stderr, "(%s) The DVD only contains %d titles.\n", __FILE__, tt_srpt->nr_of_srpts); ifoClose(vmg); DVDClose(dvd); exit(1); } title--; if (verbose) fprintf(stderr, "(%s) Title %d contains %d chapters.\n", __FILE__, title + 1, tt_srpt->title[title].nr_of_ptts); vts = ifoOpen(dvd, tt_srpt->title[title].title_set_nr); if (vts == NULL) { fprintf(stderr, "(%s) Can't open VTS info.\n", __FILE__); ifoClose(vmg); DVDClose(dvd); exit(1); } if (end > 0) { if ((end <= start) || (end > tt_srpt->title[title].nr_of_ptts)) { fprintf(stderr, "(%s) Invalid end chapter.\n", __FILE__); ifoClose(vmg); DVDClose(dvd); exit(1); } } ttn = tt_srpt->title[title].vts_ttn; vts_ptt_srpt = vts->vts_ptt_srpt; start_time = overall_time = 0; for (i = 0; i < tt_srpt->title[title].nr_of_ptts - 1; i++) { pgc_id = vts_ptt_srpt->title[ttn - 1].ptt[i].pgcn; pgn = vts_ptt_srpt->title[ttn - 1].ptt[i].pgn; cur_pgc = vts->vts_pgcit->pgci_srp[pgc_id - 1].pgc; start_cell = cur_pgc->program_map[pgn - 1] - 1; pgc_id = vts_ptt_srpt->title[ttn - 1].ptt[i + 1].pgcn; pgn = vts_ptt_srpt->title[ttn - 1].ptt[i + 1].pgn; cur_pgc = vts->vts_pgcit->pgci_srp[pgc_id - 1].pgc; end_cell = cur_pgc->program_map[pgn - 1] - 2; cur_time = 0; for (j = start_cell; j <= end_cell; j++) { dt = &cur_pgc->cell_playback[j].playback_time; hour = ((dt->hour & 0xf0) >> 4) * 10 + (dt->hour & 0x0f); minute = ((dt->minute & 0xf0) >> 4) * 10 + (dt->minute & 0x0f); second = ((dt->second & 0xf0) >> 4) * 10 + (dt->second & 0x0f); if (((dt->frame_u & 0xc0) >> 6) == 1) fps = 25.00; else fps = 29.97; dt->frame_u &= 0x3f; dt->frame_u = ((dt->frame_u & 0xf0) >> 4) * 10 + (dt->frame_u & 0x0f); ms = (double)dt->frame_u * 1000.0 / fps; cur_time += (hour * 60 * 60 * 1000 + minute * 60 * 1000 + second * 1000 + ms); } if (start == i) start_time = overall_time; if (i >= start && (i < end || end <= 0)) { fprintf(stdout, "CHAPTER%02d=%02ld:%02ld:%02ld.%03ld\n", i + 1 - start, (overall_time - start_time) / 60 / 60 / 1000, ((overall_time - start_time) / 60 / 1000) % 60, ((overall_time - start_time) / 1000) % 60, (overall_time - start_time) % 1000); fprintf(stdout, "CHAPTER%02dNAME=Chapter %02d\n", i + 1 - start, i + 1 - start); } overall_time += cur_time; } if (end <= 0 || i == end) { fprintf(stdout, "CHAPTER%02d=%02ld:%02ld:%02ld.%03ld\n", i + 1 - start, (overall_time - start_time) / 60 / 60 / 1000, ((overall_time - start_time) / 60 / 1000) % 60, ((overall_time - start_time) / 1000) % 60, (overall_time - start_time) % 1000); fprintf(stdout, "CHAPTER%02dNAME=Chapter %02d\n", i + 1 - start, i + 1 - start); } ifoClose(vts); ifoClose(vmg); DVDClose(dvd); } int main(int argc, char *argv[]) { int title = 1; int start = 0, end = 0; int i; char *source = NULL; int verbose = 0; if (argc == 1) { usage(); exit(0); } for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { usage(); exit(0); } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) verbose++; else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) { fprintf(stdout, "dvdxchap v" VERSION "\n"); exit(0); } else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--title")) { if ((i + 1) >= argc) { fprintf(stderr, "(%s) Error: -t lacks a title number.\n", __FILE__); exit(1); } title = strtol(argv[i + 1], NULL, 10); if ((errno == ERANGE) || (errno == EINVAL) || (title < 1)) { fprintf(stderr, "(%s) Error: '%s' is not a valid title number.\n", __FILE__, argv[i + 1]); exit(1); } i++; } else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--chapter")) { if ((i + 1) >= argc) { fprintf(stderr, "(%s) Error: -c lacks a chapter number.\n", __FILE__); exit(1); } if (sscanf(argv[i + 1], "%d-%d", &start, &end) < 1) { fprintf(stderr, "(%s) Error: '%s' is not a valid chapter range.\n", __FILE__, argv[i + 1]); exit(1); } if (start < 0) { end = -start; start = 0; } if ((start > end) && (end > 0)) { int tmp; tmp = start; start = end; end = tmp; } if (start > 0) start--; i++; } else { if (source != NULL) { fprintf(stderr, "(%s) Error: more than one source given.\n", __FILE__); exit(1); } source = argv[i]; } } if (source == NULL) { fprintf(stderr, "(%s) Error: No source given.\n", __FILE__); exit(1); } display_chapters(source, title, start, end, verbose); return 0; }