diff -Naur libdvdnav.orig/src/dvdnav/dvdnav.h libdvdnav/src/dvdnav/dvdnav.h --- libdvdnav.orig/src/dvdnav/dvdnav.h 2009-03-13 18:28:22.000000000 -0700 +++ libdvdnav/src/dvdnav/dvdnav.h 2009-04-30 10:56:29.000000000 -0700 @@ -279,6 +279,11 @@ dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int32_t title, int32_t part); /* + * Plays the specified title, starting from the specified program + */ +dvdnav_status_t dvdnav_program_play(dvdnav_t *this, int32_t title, int32_t pgcn, int32_t pgn); + +/* * Stores in *times an array (that the application *must* free) of * dvdtimes corresponding to the chapter times for the chosen title. * *duration will have the duration of the title @@ -320,6 +325,13 @@ int32_t *part); /* + * Return the title number, pgcn and pgn currently being played. + * A title of 0 indicates, we are in a menu. + */ +dvdnav_status_t dvdnav_current_title_program(dvdnav_t *self, int32_t *title, + int32_t *pgcn, int32_t *pgn); + +/* * Return the current position (in blocks) within the current * title and the length (in blocks) of said title. * diff -Naur libdvdnav.orig/src/navigation.c libdvdnav/src/navigation.c --- libdvdnav.orig/src/navigation.c 2009-01-08 14:57:11.000000000 -0800 +++ libdvdnav/src/navigation.c 2009-04-30 10:55:47.000000000 -0700 @@ -122,10 +122,90 @@ return DVDNAV_STATUS_ERR; } +dvdnav_status_t dvdnav_current_title_program(dvdnav_t *this, int32_t *title, int32_t *pgcn, int32_t *pgn) { + int32_t retval; + int32_t part; + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->vtsi || !this->vm->vmgi) { + printerr("Bad VM state."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + if (!this->started) { + printerr("Virtual DVD machine not started."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + if ( (this->vm->state.domain == VTSM_DOMAIN) + || (this->vm->state.domain == VMGM_DOMAIN) ) { + /* Get current Menu ID: into *part. */ + if(! vm_get_current_menu(this->vm, &part)) { + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + if (part > -1) { + *title = 0; + *pgcn = this->vm->state.pgcN; + *pgn = this->vm->state.pgN; + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + } + if (this->vm->state.domain == VTS_DOMAIN) { + retval = vm_get_current_title_part(this->vm, title, &part); + *pgcn = this->vm->state.pgcN; + *pgn = this->vm->state.pgN; + pthread_mutex_unlock(&this->vm_lock); + return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR; + } + printerr("Not in a title or menu."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; +} + dvdnav_status_t dvdnav_title_play(dvdnav_t *this, int32_t title) { return dvdnav_part_play(this, title, 1); } +dvdnav_status_t dvdnav_program_play(dvdnav_t *this, int32_t title, int32_t pgcn, int32_t pgn) { + int32_t retval; + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->vmgi) { + printerr("Bad VM state."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + if (!this->started) { + /* don't report an error but be nice */ + vm_start(this->vm); + this->started = 1; + } + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + if((title < 1) || (title > this->vm->vmgi->tt_srpt->nr_of_srpts)) { + printerr("Title out of range."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + + retval = vm_jump_title_program(this->vm, title, pgcn, pgn); + if (retval) + this->vm->hop_channel++; + pthread_mutex_unlock(&this->vm_lock); + + return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR; +} + dvdnav_status_t dvdnav_part_play(dvdnav_t *this, int32_t title, int32_t part) { int32_t retval; diff -Naur libdvdnav.orig/src/vm/vm.c libdvdnav/src/vm/vm.c --- libdvdnav.orig/src/vm/vm.c 2009-03-13 18:28:22.000000000 -0700 +++ libdvdnav/src/vm/vm.c 2009-04-30 11:07:35.000000000 -0700 @@ -83,6 +83,8 @@ static int set_PTT(vm_t *vm, int tt, int ptt); static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn); static int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part); +static int set_PROG(vm_t *vm, int tt, int pgcn, int pgn); +static int set_VTS_PROG(vm_t *vm, int vtsN, int vts_ttn, int pgcn, int pgn); static int set_FP_PGC(vm_t *vm); static int set_MENU(vm_t *vm, int menu); static int set_PGCN(vm_t *vm, int pgcN); @@ -516,6 +518,24 @@ return 1; } +int vm_jump_title_program(vm_t *vm, int title, int pgcn, int pgn) { + link_t link; + + if(!set_PROG(vm, title, pgcn, pgn)) + return 0; + /* Some DVDs do not want us to jump directly into a title and have + * PGC pre commands taking us back to some menu. Since we do not like that, + * we do not execute PGC pre commands that would do a jump. */ + /* process_command(vm, play_PGC_PG(vm, (vm->state).pgN)); */ + link = play_PGC_PG(vm, (vm->state).pgN); + if (link.command != PlayThis) + /* jump occured -> ignore it and play the PG anyway */ + process_command(vm, play_PG(vm)); + else + process_command(vm, link); + return 1; +} + int vm_jump_title_part(vm_t *vm, int title, int part) { link_t link; @@ -1644,6 +1664,42 @@ return res; } +static int set_PROG(vm_t *vm, int tt, int pgcn, int pgn) { + assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts); + return set_VTS_PROG(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr, + vm->vmgi->tt_srpt->title[tt - 1].vts_ttn, pgcn, pgn); +} + +static int set_VTS_PROG(vm_t *vm, int vtsN, int vts_ttn, int pgcn, int pgn) { + int pgcN, pgN, res, title, part; + + (vm->state).domain = VTS_DOMAIN; + + if (vtsN != (vm->state).vtsN) + if (!ifoOpenNewVTSI(vm, vm->dvd, vtsN)) /* Also sets (vm->state).vtsN */ + return 0; + + if ((vts_ttn < 1) || (vts_ttn > vm->vtsi->vts_ptt_srpt->nr_of_srpts)) { + return 0; + } + + pgcN = pgcn; + pgN = pgn; + + (vm->state).TT_PGCN_REG = pgcN; + (vm->state).TTN_REG = get_TT(vm, vtsN, vts_ttn); + assert( (vm->state.TTN_REG) != 0 ); + (vm->state).VTS_TTN_REG = vts_ttn; + (vm->state).vtsN = vtsN; /* Not sure about this one. We can get to it easily from TTN_REG */ + /* Any other registers? */ + + res = set_PGCN(vm, pgcN); /* This clobber's state.pgN (sets it to 1), but we don't want clobbering here. */ + (vm->state).pgN = pgN; + vm_get_current_title_part(vm, &title, &part); + (vm->state).PTTN_REG = part; + return res; +} + static int set_FP_PGC(vm_t *vm) { (vm->state).domain = FP_DOMAIN; if (!vm->vmgi->first_play_pgc) { diff -Naur libdvdnav.orig/src/vm/vm.h libdvdnav/src/vm/vm.h --- libdvdnav.orig/src/vm/vm.h 2009-03-13 18:28:22.000000000 -0700 +++ libdvdnav/src/vm/vm.h 2009-04-30 10:57:02.000000000 -0700 @@ -139,6 +139,7 @@ int vm_jump_pg(vm_t *vm, int pg); int vm_jump_cell_block(vm_t *vm, int cell, int block); int vm_jump_title_part(vm_t *vm, int title, int part); +int vm_jump_title_program(vm_t *vm, int title, int pgcn, int pgn); int vm_jump_top_pg(vm_t *vm); int vm_jump_next_pg(vm_t *vm); int vm_jump_prev_pg(vm_t *vm);