summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-04-10 12:29:03 +0200
committerwm4 <wm4@nowhere>2020-04-10 12:37:12 +0200
commit40c8df8a542f62126c61a8fa5bf6518275ea9e05 (patch)
tree7476bfaf72985e97f4284bf5a54276729aa58b45
parentc3f40e513b63bd484e431984135208106d13c2d3 (diff)
downloadmpv-40c8df8a542f62126c61a8fa5bf6518275ea9e05.tar.bz2
mpv-40c8df8a542f62126c61a8fa5bf6518275ea9e05.tar.xz
stream: actually drop unneeded buffer
The buffer can be larger than the normal size when "peeking" is used (such as done with some file formats, where a large number of bytes masy need to be "peeked" at the beginning, because FFmpeg). Once normal operation resumes, it's supposed to free this buffer again. Apparently this didn't happen as intended, because normal reading did have no way to discard back buffer before/while resizing the buffer. There's only a path for discarding the back buffer when actually reading. It seems like this unfortunately needs 2 code paths for discarding old data. Just put it into stream_resize_buffer(), where it's rather non-tricky (discarding can be done by adjusting the copy offset when moving data to the new allocation). The function now drops old data if it doesn't fit into the allocation. The caller must ensure that the new size is sufficient; the function signature changes only so the size of the implicitly guaranteed kept part can be checked with assert().
-rw-r--r--stream/stream.c37
1 files changed, 21 insertions, 16 deletions
diff --git a/stream/stream.c b/stream/stream.c
index 0a026713f6..0d72aa082f 100644
--- a/stream/stream.c
+++ b/stream/stream.c
@@ -264,24 +264,28 @@ static int ring_copy(struct stream *s, void *dst, int len, int pos)
// Does nothing if the size is adequate. Calling this with 0 ensures it uses the
// default buffer size if possible.
// The caller must check whether enough data was really allocated.
-// Returns false if buffer allocation failed.
-static bool stream_resize_buffer(struct stream *s, uint32_t new)
+// keep: keep at least [buf_end-keep, buf_end] (used for assert()s only)
+// new: new total size of buffer
+// returns: false if buffer allocation failed, true if reallocated or size ok
+static bool stream_resize_buffer(struct stream *s, int keep, int new)
{
- // Keep all valid buffer.
- int old_used_len = s->buf_end - s->buf_start;
- int old_pos = s->buf_cur - s->buf_start;
- new = MPMAX(new, old_used_len);
+ assert(keep >= s->buf_end - s->buf_cur);
+ assert(keep <= new);
- // This much is always required.
new = MPMAX(new, s->requested_buffer_size);
-
new = MPMIN(new, STREAM_MAX_BUFFER_SIZE);
new = mp_round_next_power_of_2(new);
+ assert(keep <= new); // can't fail (if old buffer size was valid)
+
if (new == s->buffer_mask + 1)
return true;
- MP_DBG(s, "resize stream to %d bytes\n", new);
+ int old_pos = s->buf_cur - s->buf_start;
+ int old_used_len = s->buf_end - s->buf_start;
+ int skip = old_used_len > new ? old_used_len - new : 0;
+
+ MP_DBG(s, "resize stream to %d bytes, drop %d bytes\n", new, skip);
void *nbuf = ta_alloc_size(s, new);
if (!nbuf)
@@ -289,11 +293,12 @@ static bool stream_resize_buffer(struct stream *s, uint32_t new)
int new_len = 0;
if (s->buffer)
- new_len = ring_copy(s, nbuf, new, s->buf_start);
- assert(new_len == old_used_len);
- assert(old_pos <= old_used_len);
+ new_len = ring_copy(s, nbuf, new, s->buf_start + skip);
+ assert(new_len == old_used_len - skip);
+ assert(old_pos >= skip); // "keep" too low
+ assert(old_pos - skip <= new_len);
s->buf_start = 0;
- s->buf_cur = old_pos;
+ s->buf_cur = old_pos - skip;
s->buf_end = new_len;
ta_free(s->buffer);
@@ -379,7 +384,7 @@ static int stream_create_instance(const stream_info_t *sinfo,
return r;
}
- if (!stream_resize_buffer(s, 0)) {
+ if (!stream_resize_buffer(s, 0, 0)) {
free_stream(s);
return STREAM_ERROR;
}
@@ -506,7 +511,7 @@ static bool stream_read_more(struct stream *s, int forward)
// Keep guaranteed seek-back.
int buf_old = MPMIN(s->buf_cur - s->buf_start, s->requested_buffer_size / 2);
- if (!stream_resize_buffer(s, buf_old + forward))
+ if (!stream_resize_buffer(s, buf_old + forward_avail, buf_old + forward))
return false;
int buf_alloc = s->buffer_mask + 1;
@@ -648,7 +653,7 @@ void stream_drop_buffers(stream_t *s)
s->pos = stream_tell(s);
s->buf_start = s->buf_cur = s->buf_end = 0;
s->eof = 0;
- stream_resize_buffer(s, 0);
+ stream_resize_buffer(s, 0, 0);
}
// Seek function bypassing the local stream buffer.