From 70a66b461d0e9699964cf12fea19fa696aff46ca Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 31 Jan 2014 22:40:35 +0100 Subject: cache: refuse to seek outside of cache boundaries Note that this still happens in the stream level, so we can't have nice highlevel behavior restricting seeking. Instead, if a seek leads to the demuxer requesting data outside of the cached range, the seek will simply fail. This might confuse the demuxer, and the resulting behavior is not necessarily useful. Note that this also doesn't try to skip data on a forward seek. This would just freeze the stream with slow unseekable streams. One nice thing is that stream.h has a separate function for merely skipping data (separate from seeking forward), which is pretty useful in this case: we want skipping of data to work, even if we reject seeking forward by skipping data as too expensive. This probably is or will be useful for demux_mkv.c. --- stream/cache.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/stream/cache.c b/stream/cache.c index d96f46daaf..040e30a558 100644 --- a/stream/cache.c +++ b/stream/cache.c @@ -75,6 +75,7 @@ struct priv { int64_t fill_limit; // we should fill buffer only if space>=fill_limit int64_t seek_limit; // keep filling cache if distance is less that seek limit struct byte_meta *bm; // additional per-byte metadata + bool seekable; // underlying stream is seekable struct mp_log *log; @@ -475,6 +476,7 @@ static int cache_seek(stream_t *cache, int64_t pos) { struct priv *s = cache->priv; assert(s->cache_thread_running); + int r = 1; pthread_mutex_lock(&s->mutex); @@ -482,12 +484,21 @@ static int cache_seek(stream_t *cache, int64_t pos) " (cur=%" PRId64 ") <= %" PRId64 " \n", s->min_filepos, pos, s->read_filepos, s->max_filepos); - cache->pos = s->read_filepos = pos; - s->eof = false; // so that cache_read() will actually wait for new data - pthread_cond_signal(&s->wakeup); + if (!s->seekable && pos > s->max_filepos) { + MP_ERR(s, "Attempting to seek past cached data in unseekable stream.\n"); + r = 0; + } else if (!s->seekable && pos < s->min_filepos) { + MP_ERR(s, "Attempting to seek before cached data in unseekable stream.\n"); + r = 0; + } else { + cache->pos = s->read_filepos = pos; + s->eof = false; // so that cache_read() will actually wait for new data + pthread_cond_signal(&s->wakeup); + } + pthread_mutex_unlock(&s->mutex); - return 1; + return r; } static int cache_control(stream_t *cache, int cmd, void *arg) @@ -600,6 +611,9 @@ int stream_cache_init(stream_t *cache, stream_t *stream, int64_t size, if (min > s->buffer_size - s->fill_limit) min = s->buffer_size - s->fill_limit; + s->seekable = (stream->flags & MP_STREAM_SEEK) == MP_STREAM_SEEK && + stream->end_pos > 0; + if (pthread_create(&s->cache_thread, NULL, cache_thread, s) != 0) { MP_ERR(s, "Starting cache process/thread failed: %s.\n", strerror(errno)); -- cgit v1.2.3