aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJoachim Breuer <git@jmbreuer.net>2013-07-11 09:17:14 +0200
committerJoachim Breuer <git@jmbreuer.net>2013-07-13 09:05:24 +0200
commit24d9d5005652410baf200599a9ba966d66b90e44 (patch)
tree188692aac7152b8de03a22443a3fe4a881156c77 /lib
parentcc0ebb6db6f615dcb14e79b4b797bb3e711d669b (diff)
Add diff in lib/libdvd/patches for tracking against upstream
Diffstat (limited to 'lib')
-rw-r--r--lib/libdvd/patches/libdvdnav-accurate_seek.diff677
1 files changed, 677 insertions, 0 deletions
diff --git a/lib/libdvd/patches/libdvdnav-accurate_seek.diff b/lib/libdvd/patches/libdvdnav-accurate_seek.diff
new file mode 100644
index 0000000000..a97dccee68
--- /dev/null
+++ b/lib/libdvd/patches/libdvdnav-accurate_seek.diff
@@ -0,0 +1,677 @@
+More accurate seeking with libdvdnav
+
+applied to current codebase from:
+ http://lists.mplayerhq.hu/pipermail/dvdnav-discuss/2011-November/001612.html
+
+full credit goes to gnosygnu, see
+ http://forum.videolan.org/viewtopic.php?f=32&t=76308&start=20#p316583
+
+related tickets:
+ http://trac.xbmc.org/ticket/12212
+ http://trac.xbmc.org/ticket/14493
+
+diff --git a/libdvdnav/src/dvdnav/dvdnav.h b/libdvdnav/src/dvdnav/dvdnav.h
+index 359b951..ca3f720 100644
+--- a/libdvdnav/src/dvdnav/dvdnav.h
++++ b/libdvdnav/src/dvdnav/dvdnav.h
+@@ -373,6 +373,14 @@ dvdnav_status_t dvdnav_sector_search(dvdnav_t *self,
+ int64_t dvdnav_get_current_time(dvdnav_t *self);
+
+ /*
++ * Find the nearest vobu and jump to it
++ *
++ * Alternative to dvdnav_time_search
++ */
++dvdnav_status_t dvdnav_jump_to_sector_by_time(dvdnav_t *this,
++ uint64_t time_in_pts_ticks);
++
++/*
+ * Stop playing the current position and start playback of the title
+ * from the specified timecode.
+ *
+diff --git a/libdvdnav/src/dvdnav_internal.h b/libdvdnav/src/dvdnav_internal.h
+index d64a5ba..c60752f 100644
+--- a/libdvdnav/src/dvdnav_internal.h
++++ b/libdvdnav/src/dvdnav_internal.h
+@@ -125,6 +125,42 @@ typedef struct {
+ } ATTRIBUTE_PACKED spu_status_t;
+ #endif
+
++/*
++ * Describes a given time, and the closest sector, vobu and tmap index
++ */
++typedef struct {
++ uint64_t time;
++ uint32_t sector;
++ uint32_t vobu_idx;
++ int32_t tmap_idx;
++} dvdnav_pos_data_t;
++
++/*
++ * Encapsulates cell data
++ */
++typedef struct {
++ int32_t idx;
++ dvdnav_pos_data_t *bgn;
++ dvdnav_pos_data_t *end;
++} dvdnav_cell_data_t;
++
++/*
++ * Encapsulates common variables used by internal functions of jump_to_time
++ */
++typedef struct {
++ vobu_admap_t *admap;
++ int32_t admap_len;
++ vts_tmap_t *tmap;
++ int32_t tmap_len;
++ int32_t tmap_interval;
++} dvdnav_jump_args_t;
++
++/*
++ * Utility constants for jump_to_time
++ */
++#define TMAP_IDX_EDGE_BGN -1
++#define TMAP_IDX_EDGE_END -2
++
+ typedef struct dvdnav_vobu_s {
+ int32_t vobu_start; /* Logical Absolute. MAX needed is 0x300000 */
+ int32_t vobu_length;
+diff --git a/libdvdnav/src/searching.c b/libdvdnav/src/searching.c
+index a5e48fe..2df7212 100644
+--- a/libdvdnav/src/searching.c
++++ b/libdvdnav/src/searching.c
+@@ -36,6 +36,7 @@
+ #include "vm/decoder.h"
+ #include "vm/vm.h"
+ #include "dvdnav_internal.h"
++#include <dvdread/ifo_read.h>
+
+ /*
+ #define LOG_DEBUG
+@@ -805,3 +806,586 @@ dvdnav_status_t dvdnav_set_state(dvdnav_t *this, dvd_state_t *save_state)
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
++
++
++
++/* Get an admap and admap_len */
++static vobu_admap_t* dvdnav_admap_get(dvdnav_t *this, dvd_state_t *state,
++ int32_t *admap_len) {
++ vobu_admap_t *admap = NULL;
++ switch(state->domain) {
++ case FP_DOMAIN:
++ case VMGM_DOMAIN:
++ admap = this->vm->vmgi->menu_vobu_admap;
++ break;
++ case VTSM_DOMAIN:
++ admap = this->vm->vtsi->menu_vobu_admap;
++ break;
++ case VTS_DOMAIN:
++ admap = this->vm->vtsi->vts_vobu_admap;
++ break;
++ default: {
++ fprintf(MSG_OUT, "Unknown domain");
++ return NULL;
++ }
++ }
++ if (admap == NULL) return NULL;
++
++ *admap_len = (admap->last_byte + 1 - VOBU_ADMAP_SIZE) / VOBU_ADMAP_SIZE;
++ if (*admap_len <= 0) {
++ fprintf(MSG_OUT, "admap_len <= 0");
++ return NULL;
++ }
++ return admap;
++}
++
++/* Get a tmap, tmap_len and tmap_interval */
++static vts_tmap_t* dvdnav_tmap_get(dvdnav_t *this, dvd_state_t *state,
++ int32_t *tmap_len, int32_t *tmap_interval) {
++ int32_t vts_idx = 0;
++ domain_t domain;
++ ifo_handle_t *ifo = NULL;
++ vts_tmapt_t *tmapt = NULL;
++ uint16_t tmap_count = 0;
++ int32_t pgcN = 0;
++ vts_tmap_t *tmap = NULL;
++ int32_t result = 0;
++
++ vts_idx = state->vtsN;
++ domain = state->domain;
++ switch(domain) {
++ case FP_DOMAIN:
++ case VTSM_DOMAIN:
++ case VMGM_DOMAIN: {
++ ifo = this->vm->vmgi;
++ break;
++ }
++ case VTS_DOMAIN: {
++ ifo = this->vm->vtsi;
++ break;
++ }
++ default: {
++ fprintf(MSG_OUT, "unknown domain for tmap");
++ return NULL;
++ }
++ }
++ if (ifo == NULL) return NULL;
++ tmapt = ifo->vts_tmapt;
++ /* HACK: ifo->vts_tmapt is NULL b/c ifo_read.c never loads it
++ * load ifo->vts_tmapt directly*/
++ if (tmapt == NULL) {
++ result = ifoRead_VTS_TMAPT(ifo);
++ if (!result) {
++ return NULL;
++ }
++ tmapt = ifo->vts_tmapt;
++ if (tmapt == NULL) return NULL;
++ }
++
++ tmap_count = tmapt->nr_of_tmaps;
++ pgcN = state->pgcN - 1; /* -1 b/c pgcN is base1 */
++ if (pgcN < 0) {
++ fprintf(MSG_OUT, "pgcN < 0");
++ return NULL;
++ }
++
++ /* get tmap */
++ switch(domain) {
++ case FP_DOMAIN:
++ case VMGM_DOMAIN:
++ case VTSM_DOMAIN: {
++ if (tmap_count == 0) {
++ fprintf(MSG_OUT, "tmap_count == 0");
++ return NULL;
++ }
++ tmap = &tmapt->tmap[0]; /* ASSUME: vmgi only has one time map */
++ break;
++ }
++ case VTS_DOMAIN: {
++ if (pgcN >= tmap_count) {
++ fprintf(MSG_OUT, "pgcN >= tmap_count; pgcN=%i tmap_count=%i",
++ pgcN, tmap_count);
++ return NULL;
++ }
++ tmap = &tmapt->tmap[pgcN];
++ break;
++ }
++ }
++ if (tmap == NULL) return NULL;
++
++ /* tmap->tmu is in seconds; convert to millisecs */
++ *tmap_interval = tmap->tmu * 1000;
++ if (*tmap_interval == 0) {
++ fprintf(MSG_OUT, "tmap_interval == 0");
++ return NULL;
++ }
++ *tmap_len = tmap->nr_of_entries;
++ if (*tmap_len == 0) {
++ fprintf(MSG_OUT, "tmap_len == 0");
++ return NULL;
++ }
++ return tmap;
++}
++
++/* Get a sector from a tmap */
++static int32_t dvdnav_tmap_get_entry(vts_tmap_t *tmap, uint16_t tmap_len,
++ int32_t idx, uint32_t *sector) {
++ /* tmaps start at idx 0 which represents a sector at time 1 * tmap_interval
++ * this creates a "fake" tmap index at idx -1 for sector 0 */
++ if (idx == TMAP_IDX_EDGE_BGN) {
++ *sector = 0;
++ return 1;
++ }
++ if (idx < TMAP_IDX_EDGE_BGN || idx >= tmap_len) {
++ fprintf(MSG_OUT, "idx out of bounds idx=%i %i", idx, tmap_len);
++ return 0;
++ }
++ /* 0x7fffffff unsets discontinuity bit if present */
++ *sector = tmap->map_ent[idx] & 0x7fffffff;
++ return 1;
++}
++
++/* Do a binary search for earlier admap index near find_sector */
++static int32_t dvdnav_admap_search(vobu_admap_t *admap, uint32_t admap_len,
++ uint32_t find_sector, uint32_t *vobu) {
++ int32_t adj = 1;
++ int32_t prv_pos = 0;
++ int32_t prv_len = admap_len;
++ int32_t cur_len = 0;
++ int32_t cur_idx = 0;
++ uint32_t cur_sector = 0;
++ while (1) {
++ cur_len = prv_len / 2;
++ /* need to add 1 when prv_len == 3 (cur_len shoud go to 2, not 1) */
++ if (prv_len % 2 == 1) ++cur_len;
++ cur_idx = prv_pos + (cur_len * adj);
++ if (cur_idx < 0) cur_idx = 0;
++ else if (cur_idx >= admap_len) cur_idx = admap_len - 1;
++
++ cur_sector = admap->vobu_start_sectors[cur_idx];
++ if (find_sector < cur_sector) adj = -1;
++ else if (find_sector > cur_sector) adj = 1;
++ else if (find_sector == cur_sector) {
++ *vobu = cur_idx;
++ return 1;
++ }
++ if (cur_len == 1) {/* no smaller intervals left */
++ if (adj == -1) {/* last comparison was greater; take lesser */
++ cur_idx -= 1;
++ cur_sector = admap->vobu_start_sectors[cur_idx];
++ }
++ *vobu = cur_idx;
++ return 1;
++ }
++ prv_len = cur_len;
++ prv_pos = cur_idx;
++ }
++}
++
++/* Do a binary search for the earlier tmap entry near find_sector */
++static int32_t dvdnav_tmap_search(vts_tmap_t *tmap, uint32_t tmap_len,
++ uint32_t find_sector, int32_t *tmap_idx, uint32_t *sector) {
++ int32_t adj = 1;
++ int32_t prv_pos = 0;
++ int32_t prv_len = tmap_len;
++ int32_t result = 0;
++ int32_t cur_len = 0;
++ int32_t cur_idx = 0;
++ uint32_t cur_sector = 0;
++ while (1) {
++ cur_len = prv_len / 2;
++ /* need to add 1 when prv_len == 3 (cur_len shoud go to 2, not 1) */
++ if (prv_len % 2 == 1) ++cur_len;
++ cur_idx = prv_pos + (cur_len * adj);
++ if (cur_idx < 0) cur_idx = 0;
++ else if (cur_idx >= tmap_len) cur_idx = tmap_len - 1;
++ cur_sector = 0;
++ result = dvdnav_tmap_get_entry(tmap, tmap_len, cur_idx, &cur_sector);
++ if (!result) return 0;
++ if (find_sector < cur_sector) adj = -1;
++ else if (find_sector > cur_sector) adj = 1;
++ else if (find_sector == cur_sector) {
++ *tmap_idx = cur_idx;
++ *sector = cur_sector;
++ return 1;
++ }
++ if (cur_len == 1) {/* no smaller intervals left */
++ if (adj == -1) {/* last comparison was greater; take lesser */
++ if (cur_idx == 0) { /* fake tmap index for sector 0 */
++ cur_idx = TMAP_IDX_EDGE_BGN;
++ cur_sector = 0;
++ }
++ else {
++ cur_idx -= 1;
++ result = dvdnav_tmap_get_entry(tmap, tmap_len, cur_idx, &cur_sector);
++ if (!result) return 0;
++ }
++ }
++ *tmap_idx = cur_idx;
++ *sector = cur_sector;
++ return 1;
++ }
++ prv_len = cur_len;
++ prv_pos = cur_idx;
++ }
++}
++
++/* Find the cell for a given sector */
++static int32_t dvdnav_cell_find(dvdnav_t *this, dvd_state_t *state,
++ uint64_t find_val, dvdnav_cell_data_t *cell_data) {
++ uint32_t cells_len = 0;
++ uint32_t cells_bgn = 0;
++ uint32_t cells_end = 0;
++ uint32_t cell_idx = 0;
++ pgc_t *pgc = NULL;
++ int pgN = 0;
++ cell_playback_t *cell = NULL;
++ int found = 0;
++
++ pgc = state->pgc;
++ if (pgc == NULL) return 0;
++ cells_len = pgc->nr_of_cells;
++ if (cells_len == 0) {
++ fprintf(MSG_OUT, "cells_len == 0");
++ return 0;
++ }
++
++ /* get cells_bgn, cells_end */
++ if (this->pgc_based) {
++ cells_bgn = 1;
++ cells_end = cells_len;
++ }
++ else {
++ pgN = state->pgN;
++ cells_bgn = pgc->program_map[pgN - 1]; /* -1 b/c pgN is 1 based? */
++ if (pgN < pgc->nr_of_programs) {
++ cells_end = pgc->program_map[pgN] - 1;
++ }
++ else {
++ cells_end = cells_len;
++ }
++ }
++
++ /* search cells */
++ for (cell_idx = cells_bgn; cell_idx <= cells_end; cell_idx++) {
++ cell = &(pgc->cell_playback[cell_idx - 1]); /* -1 b/c cell is base1 */
++ /* if angle block, only consider first angleBlock
++ * (others are "redundant" for purpose of search) */
++ if ( cell->block_type == BLOCK_TYPE_ANGLE_BLOCK
++ && cell->block_mode != BLOCK_MODE_FIRST_CELL) {
++ continue;
++ }
++ cell_data->bgn->sector = cell->first_sector;
++ cell_data->end->sector = cell->last_sector;
++
++ /* 90 pts to ms */
++ cell_data->end->time += (dvdnav_convert_time(&cell->playback_time) / 90);
++ if ( find_val >= cell_data->bgn->time
++ && find_val <= cell_data->end->time) {
++ found = 1;
++ break;
++ }
++ cell_data->bgn->time = cell_data->end->time;
++ }
++
++ /* found cell: set var */
++ if (found) {
++ cell_data->idx = cell_idx;
++ }
++ else
++ fprintf(MSG_OUT, "cell not found; find=%"PRId64"", find_val);
++ return found;
++}
++
++/* Given two sectors and a fraction, calc the corresponding vobu */
++static int32_t dvdnav_admap_interpolate_vobu(dvdnav_jump_args_t *args,
++ dvdnav_pos_data_t *bgn, dvdnav_pos_data_t *end, uint32_t fraction,
++ uint32_t *jump_sector) {
++ int32_t result = 0;
++ uint32_t vobu_len = 0;
++ uint32_t vobu_adj = 0;
++ uint32_t vobu_idx = 0;
++
++ /* get bgn->vobu_idx */
++ result = dvdnav_admap_search(args->admap, args->admap_len,
++ bgn->sector, &bgn->vobu_idx);
++ if (!result) {
++ fprintf(MSG_OUT, "admap_interpolate: could not find sector_bgn");
++ return 0;
++ }
++
++ /* get end->vobu_idx */
++ result = dvdnav_admap_search(args->admap, args->admap_len,
++ end->sector, &end->vobu_idx);
++ if (!result) {
++ fprintf(MSG_OUT, "admap_interpolate: could not find sector_end");
++ return 0;
++ }
++
++ vobu_len = end->vobu_idx - bgn->vobu_idx;
++ /* +500 to round up else 74% of a 4 sec interval = 2 sec */
++ vobu_adj = ((fraction * vobu_len) + 500) / 1000;
++ /* HACK: need to add +1, or else will land too soon (not sure why) */
++ vobu_adj++;
++ vobu_idx = bgn->vobu_idx + vobu_adj;
++ if (vobu_idx >= args->admap_len) {
++ fprintf(MSG_OUT, "admap_interpolate: vobu_idx >= admap_len");
++ return 0;
++ }
++ *jump_sector = args->admap->vobu_start_sectors[vobu_idx];
++ return 1;
++}
++
++/* Given two tmap entries and a time, calc the time for the lo tmap entry */
++static int32_t dvdnav_tmap_calc_time_for_tmap_entry(dvdnav_jump_args_t *args,
++ dvdnav_pos_data_t *lo, dvdnav_pos_data_t *hi,
++ dvdnav_pos_data_t *pos, uint64_t *out_time) {
++ int32_t result = 0;
++ uint32_t vobu_pct = 0;
++ uint64_t time_adj = 0;
++
++ if (lo->sector == hi->sector) {
++ fprintf(MSG_OUT, "lo->sector == hi->sector: %i", lo->sector);
++ return 0;
++ }
++
++ /* get vobus corresponding to lo, hi, pos */
++ result = dvdnav_admap_search(args->admap, args->admap_len,
++ lo->sector, &lo->vobu_idx);
++ if (!result) {
++ fprintf(MSG_OUT, "lo->vobu: lo->sector=%i", lo->sector);
++ return 0;
++ }
++ result = dvdnav_admap_search(args->admap, args->admap_len,
++ hi->sector, &hi->vobu_idx);
++ if (!result) {
++ fprintf(MSG_OUT, "hi->vobu: hi->sector=%i", hi->sector);
++ return 0;
++ }
++ result = dvdnav_admap_search(args->admap, args->admap_len,
++ pos->sector, &pos->vobu_idx);
++ if (!result) {
++ fprintf(MSG_OUT, "pos->vobu: pos->sector=%i", pos->sector);
++ return 0;
++ }
++
++ /* calc position of cell relative to lo */
++ vobu_pct = ((pos->vobu_idx - lo->vobu_idx) * 1000)
++ / ( hi->vobu_idx - lo->vobu_idx);
++ if (vobu_pct < 0 || vobu_pct > 1000) {
++ fprintf(MSG_OUT, "vobu_pct must be between 0 and 1000");
++ return 0;
++ }
++
++ /* calc time of lo */
++ time_adj = (uint64_t)((args->tmap_interval * vobu_pct) / 1000);
++ *out_time = pos->time - time_adj;
++ return 1;
++}
++
++/* Find the tmap entries on either side of a given sector */
++static int32_t dvdnav_tmap_get_entries_for_sector(dvdnav_t *this,
++ dvd_state_t *state, dvdnav_jump_args_t *args,
++ dvdnav_cell_data_t *cell_data, uint32_t find_sector,
++ dvdnav_pos_data_t *lo, dvdnav_pos_data_t *hi) {
++ int32_t result = 0;
++
++ result = dvdnav_tmap_search(args->tmap, args->tmap_len, find_sector,
++ &lo->tmap_idx, &lo->sector);
++ if (!result) {
++ fprintf(MSG_OUT, "could not find lo idx: %i", find_sector);
++ return 0;
++ }
++
++ /* HACK: Most DVDs have a tmap that starts at sector 0
++ * However, some have initial dummy cells that are not seekable
++ * (restricted = y).
++ * These cells will throw off the tmap calcs when in the first playable cell.
++ * For now, assume that lo->sector is equal to the cell->bgn->sector
++ * Note that for most DVDs this will be 0
++ * (Since they will have no dummy cells and cell 1 will start at sector 0)
++ */
++ if (lo->tmap_idx == TMAP_IDX_EDGE_BGN) {
++ lo->sector = cell_data->bgn->sector;
++ }
++
++ if (lo->tmap_idx == args->tmap_len - 1) {
++ /* lo is last tmap entry; "fake" entry for one beyond
++ * and mark it with cell_end_sector */
++ hi->tmap_idx = TMAP_IDX_EDGE_END;
++ hi->sector = cell_data->end->sector;
++ }
++ else {
++ hi->tmap_idx = lo->tmap_idx + 1;
++ result = dvdnav_tmap_get_entry(args->tmap, args->tmap_len,
++ hi->tmap_idx, &hi->sector);
++ if (!result) {
++ fprintf(MSG_OUT, "could not find hi idx: %i", find_sector);
++ return 0;
++ }
++ }
++ return 1;
++}
++
++/* Find the nearest vobu by using the tmap */
++static int32_t dvdnav_find_vobu_by_tmap(dvdnav_t *this, dvd_state_t *state,
++ dvdnav_jump_args_t *args, dvdnav_cell_data_t *cell_data,
++ dvdnav_pos_data_t *jump) {
++ uint64_t seek_offset = 0;
++ uint32_t seek_idx = 0;
++ int32_t result = 0;
++ dvdnav_pos_data_t *cell_bgn_lo = NULL;
++ dvdnav_pos_data_t *cell_bgn_hi = NULL;
++ dvdnav_pos_data_t *jump_lo = NULL;
++ dvdnav_pos_data_t *jump_hi = NULL;
++
++ /* get tmap, tmap_len, tmap_interval */
++ args->tmap = dvdnav_tmap_get(this, state,
++ &args->tmap_len, &args->tmap_interval);
++ if (args->tmap == NULL) return 0;
++
++ /* get tmap entries on either side of cell_bgn */
++ cell_bgn_lo = &(dvdnav_pos_data_t){0};
++ cell_bgn_hi = &(dvdnav_pos_data_t){0};
++ result = dvdnav_tmap_get_entries_for_sector(this, state, args, cell_data,
++ cell_data->bgn->sector, cell_bgn_lo, cell_bgn_hi);
++ if (!result) return 0;
++
++ /* calc time of cell_bgn_lo */
++ result = dvdnav_tmap_calc_time_for_tmap_entry(args, cell_bgn_lo, cell_bgn_hi,
++ cell_data->bgn, &cell_bgn_lo->time);
++ if (!result) return 0;
++
++ /* calc time of jump_time relative to cell_bgn_lo */
++ seek_offset = jump->time - cell_bgn_lo->time;
++ seek_idx = (uint32_t)(seek_offset / args->tmap_interval);
++ uint32_t seek_remainder = seek_offset - (seek_idx * args->tmap_interval);
++ uint32_t seek_pct = (seek_remainder * 1000) / args->tmap_interval;
++
++ /* get tmap entries on either side of jump_time */
++ jump_lo = &(dvdnav_pos_data_t){0};
++ jump_hi = &(dvdnav_pos_data_t){0};
++
++ /* if seek_idx == 0, then tmap_indexes are the same, do not re-get
++ * also, note cell_bgn_lo will already have sector if TMAP_IDX_EDGE_BGN */
++ if (seek_idx == 0) {
++ jump_lo = cell_bgn_lo;
++ jump_hi = cell_bgn_hi;
++ }
++ else {
++ jump_lo->tmap_idx = (uint32_t)(cell_bgn_lo->tmap_idx + seek_idx);
++ result = dvdnav_tmap_get_entry(args->tmap, args->tmap_len,
++ jump_lo->tmap_idx, &jump_lo->sector);
++ if (!result) return 0;
++
++ /* +1 handled by dvdnav_tmap_get_entry */
++ jump_hi->tmap_idx = jump_lo->tmap_idx + 1;
++ result = dvdnav_tmap_get_entry(args->tmap, args->tmap_len,
++ jump_hi->tmap_idx, &jump_hi->sector);
++ if (!result) return 0;
++ }
++
++ /* interpolate sector */
++ result = dvdnav_admap_interpolate_vobu(args, jump_lo, jump_hi,
++ seek_pct, &jump->sector);
++
++ return result;
++}
++
++/* Find the nearest vobu by using the cell boundaries */
++static int32_t dvdnav_find_vobu_by_cell_boundaries(dvdnav_t *this,
++ dvdnav_jump_args_t *args, dvdnav_cell_data_t *cell_data,
++ dvdnav_pos_data_t *jump) {
++ uint64_t jump_offset = 0;
++ uint64_t cell_len = 0;
++ uint32_t jump_pct = 0;
++ int32_t result = 0;
++
++ /* get jump_offset */
++ jump_offset = jump->time - cell_data->bgn->time;
++ if (jump_offset < 0) {
++ fprintf(MSG_OUT, "jump_offset < 0");
++ return 0;
++ }
++ cell_len = cell_data->end->time - cell_data->bgn->time;
++ if (cell_len < 0) {
++ fprintf(MSG_OUT, "cell_len < 0");
++ return 0;
++ }
++ jump_pct = (jump_offset * 1000) / cell_len;
++
++ /* get sector */
++ /* NOTE: end cell sector in VTS_PGC is last sector of cell
++ * this last sector is not the start of a VOBU
++ * +1 to get sector that is the start of a VOBU
++ * start of a VOBU is needed in order to index into admap */
++ cell_data->end->sector += 1;
++ result = dvdnav_admap_interpolate_vobu(args,
++ cell_data->bgn, cell_data->end, jump_pct, &jump->sector);
++ if (!result) {
++ fprintf(MSG_OUT, "find_by_admap.interpolate");
++ return 0;
++ }
++ return 1;
++}
++
++dvdnav_status_t dvdnav_jump_to_sector_by_time(dvdnav_t *this,
++ uint64_t time_in_pts_ticks) {
++ int32_t result = 1;
++ dvd_state_t *state = NULL;
++ uint32_t sector_off = 0;
++ dvdnav_pos_data_t *jump = NULL;
++ dvdnav_cell_data_t *cell_data = NULL;
++ dvdnav_jump_args_t *args = NULL;
++
++ jump = &(dvdnav_pos_data_t){0};
++ /* convert time to milliseconds */
++ jump->time = time_in_pts_ticks / 90;
++
++ /* get variables that will be used across both functions */
++ state = &(this->vm->state);
++ if (state == NULL) goto exit;
++
++ /* get cell info */
++ cell_data = &(dvdnav_cell_data_t){0};
++ cell_data->bgn = &(dvdnav_pos_data_t){0};
++ cell_data->end = &(dvdnav_pos_data_t){0};
++ result = dvdnav_cell_find(this, state, jump->time, cell_data);
++ if (!result) goto exit;
++
++ /* get admap */
++ args = &(dvdnav_jump_args_t){0};
++ args->admap = dvdnav_admap_get(this, state, &args->admap_len);
++ if (args->admap == NULL) goto exit;
++
++ /* find sector */
++ result = dvdnav_find_vobu_by_tmap(this, state, args, cell_data, jump);
++ if (!result) {// bad tmap; interpolate over cell
++ result = dvdnav_find_vobu_by_cell_boundaries(this, args, cell_data, jump);
++ if (!result) {
++ goto exit;
++ }
++ }
++
++#ifdef LOG_DEBUG
++ fprintf(MSG_OUT, "libdvdnav: seeking to time=%lu\n", jump->time);
++ fprintf(MSG_OUT, "libdvdnav: Before cellN=%u blockN=%u\n", state->cellN, state->blockN);
++#endif
++
++ /* jump to sector */
++ sector_off = jump->sector - cell_data->bgn->sector;
++ this->cur_cell_time = 0;
++ if (vm_jump_cell_block(this->vm, cell_data->idx, sector_off)) {
++ pthread_mutex_lock(&this->vm_lock);
++ this->vm->hop_channel += HOP_SEEK;
++ pthread_mutex_unlock(&this->vm_lock);
++ result = 1;
++ }
++
++#ifdef LOG_DEBUG
++ fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u\n", state->cellN, state->blockN);
++#endif
++
++exit:
++ return result;
++}