aboutsummaryrefslogtreecommitdiffhomepage
path: root/stream
diff options
context:
space:
mode:
authorGravatar wm4 <wm4@nowhere>2015-01-19 21:25:30 +0100
committerGravatar wm4 <wm4@nowhere>2015-01-19 21:30:05 +0100
commit7a7d8d50e23776fb3fb863df9b6cd0b84cf85d92 (patch)
tree1fbb58fa6bc4ee1fcf09f62bc6395ab840ca4964 /stream
parent966f0a41a4182cf4027a5d49b248a26ff49368f3 (diff)
dvd: try to improve seeking
libdvdnav is garbage. Seeking by time is incredibly inexact, which is in part due to the fact that it does not use the DVD seek tables. Instead, it assumes CBR for certain ranges within the DVD, which makes especially small seeks unreliable. I have no good fix for this, other than hacking libdvdnav (I'd rather prefer to remove mpv DVD support completely than doing this). So here's a shitty hack that tries to workaround these problems. A basic observation is that seeking in VLC seems to work quite well; however it seems to be based on seeking by blocks (unless there is a subtle "trick" I didn't see in the source code). mpv usually seeks by timestamps, so this is not an option for us. However, we can pretend we are doing this in the DVD layer. The previous commit added a way to pass through relative seeks. This commit uses the relative seek. STREAM_CTRL_SEEK_TO_TIME is backwards compatible (there's still dvdread and bluray), so most code is about extracing the relative seek information and turning it into a block seek. (Another way would have been using SEEK_FACTOR stuff, but that would probably make for a less reliable way to handle this situation.) Additionally, if a hr-seek is done, add an offset by 10 seconds. As long as the error done by libdvdnav is not worse, this should help with hr- seeks - although it makes them much slower.
Diffstat (limited to 'stream')
-rw-r--r--stream/stream_dvdnav.c44
1 files changed, 41 insertions, 3 deletions
diff --git a/stream/stream_dvdnav.c b/stream/stream_dvdnav.c
index cfbf438cf0..6f381c612f 100644
--- a/stream/stream_dvdnav.c
+++ b/stream/stream_dvdnav.c
@@ -565,18 +565,56 @@ static int control(stream_t *stream, int cmd, void *arg)
return STREAM_OK;
}
case STREAM_CTRL_SEEK_TO_TIME: {
- double d = *(double *)arg;
+ double *args = arg;
+ double d = args[0]; // absolute target timestamp
+ double r = args[1]; // if not SEEK_ABSOLUTE, the base time for d
+ int flags = args[2]; // from SEEK_* flags (demux.h)
+ if (flags & SEEK_HR)
+ d -= 10; // fudge offset; it's a hack, because fuck libdvd*
int64_t tm = (int64_t)(d * 90000);
if (tm < 0)
tm = 0;
if (priv->duration && tm >= (priv->duration * 90))
tm = priv->duration * 90 - 1;
- MP_VERBOSE(stream, "seek to PTS %f (%"PRId64")\n", d, tm);
- if (dvdnav_time_search(dvdnav, tm) != DVDNAV_STATUS_OK)
+ uint32_t pos, len;
+ if (dvdnav_get_position(dvdnav, &pos, &len) != DVDNAV_STATUS_OK)
break;
+ // The following is convoluted, because we have to translate between
+ // dvdnav's block/CBR-based seeking bullshit, and the player's
+ // timestamp-based high-level machinery.
+ if (!(flags & SEEK_ABSOLUTE) && !(flags & SEEK_HR) && priv->duration > 0)
+ {
+ int dir = (flags & SEEK_BACKWARD) ? -1 : 1;
+ // The user is making a relative seek (translated to absolute),
+ // and we try not to get the user stuck on "boundaries". So try
+ // to do block based seeks, which should workaround libdvdnav's
+ // terrible CBR-based seeking.
+ d -= r; // relative seek amount in seconds
+ d = d / (priv->duration / 1000.0) * len; // d is now in blocks
+ d += pos; // absolute target in blocks
+ if (dir > 0)
+ d = MPMAX(d, pos + 1.0);
+ if (dir < 0)
+ d = MPMIN(d, pos - 1.0);
+ d += 0.5; // round
+ uint32_t target = MPCLAMP(d, 0, len);
+ MP_VERBOSE(stream, "seek from block %lu to %lu, dir=%d\n",
+ (unsigned long)pos, (unsigned long)target, dir);
+ if (dvdnav_sector_search(dvdnav, target, SEEK_SET) != DVDNAV_STATUS_OK)
+ break;
+ } else {
+ // "old" method, should be good enough for large seeks. Used for
+ // hr-seeks (with fudge offset), because I fear that block-based
+ // seeking might be off too far for large jumps.
+ MP_VERBOSE(stream, "seek to PTS %f (%"PRId64")\n", d, tm);
+ if (dvdnav_time_search(dvdnav, tm) != DVDNAV_STATUS_OK)
+ break;
+ }
stream_drop_buffers(stream);
d = dvdnav_get_current_time(dvdnav) / 90000.0f;
MP_VERBOSE(stream, "landed at: %f\n", d);
+ if (dvdnav_get_position(dvdnav, &pos, &len) == DVDNAV_STATUS_OK)
+ MP_VERBOSE(stream, "block: %lu\n", (unsigned long)pos);
return STREAM_OK;
}
case STREAM_CTRL_GET_NUM_ANGLES: {