aboutsummaryrefslogtreecommitdiff
path: root/lib/libdvd
diff options
context:
space:
mode:
authorJoachim Breuer <git@jmbreuer.net>2013-07-09 18:23:35 +0200
committerJoachim Breuer <git@jmbreuer.net>2013-07-13 09:05:24 +0200
commitcc0ebb6db6f615dcb14e79b4b797bb3e711d669b (patch)
tree0223f90f105b09dac5f11cf68e6df3e41bcee8fb /lib/libdvd
parent296093925468c2d40d433d4e66a07e0ee6fb12e5 (diff)
More accurate seeking with libdvdnav
http://trac.xbmc.org/ticket/12212 applied to current codebase full credit should go to gnosygnu, see http://forum.videolan.org/viewtopic.php?f=32&t=76308&start=20#p316583
Diffstat (limited to 'lib/libdvd')
-rw-r--r--lib/libdvd/libdvdnav/src/dvdnav/dvdnav.h8
-rw-r--r--lib/libdvd/libdvdnav/src/dvdnav_internal.h36
-rw-r--r--lib/libdvd/libdvdnav/src/searching.c584
3 files changed, 628 insertions, 0 deletions
diff --git a/lib/libdvd/libdvdnav/src/dvdnav/dvdnav.h b/lib/libdvd/libdvdnav/src/dvdnav/dvdnav.h
index 359b951aad..ca3f720ba3 100644
--- a/lib/libdvd/libdvdnav/src/dvdnav/dvdnav.h
+++ b/lib/libdvd/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/lib/libdvd/libdvdnav/src/dvdnav_internal.h b/lib/libdvd/libdvdnav/src/dvdnav_internal.h
index d64a5ba7cd..c60752fdf0 100644
--- a/lib/libdvd/libdvdnav/src/dvdnav_internal.h
+++ b/lib/libdvd/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/lib/libdvd/libdvdnav/src/searching.c b/lib/libdvd/libdvdnav/src/searching.c
index a5e48fe7da..2df7212d3b 100644
--- a/lib/libdvd/libdvdnav/src/searching.c
+++ b/lib/libdvd/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;
+}