stream: turn into a ring buffer, make size configurable

In some corner cases (see #6802), it can be beneficial to use a larger
stream buffer size. Use this as argument to rewrite everything for no
reason.

Turn stream.c itself into a ring buffer, with configurable size. The
latter would have been easily achievable with minimal changes, and the
ring buffer is the hard part. There is no reason to have a ring buffer
at all, except possibly if ffmpeg don't fix their awful mp4 demuxer, and
some subtle issues with demux_mkv.c wanting to seek back by small
offsets (the latter was handled with small stream_peek() calls, which
are unneeded now).

In addition, this turns small forward seeks into reads (where data is
simply skipped). Before this commit, only stream_skip() did this (which
also mean that stream_skip() simply calls stream_seek() now).

Replace all stream_peek() calls with something else (usually
stream_read_peek()). The function was a problem, because it returned a
pointer to the internal buffer, which is now a ring buffer with
wrapping. The new function just copies the data into a buffer, and in
some cases requires callers to dynamically allocate memory. (The most
common case, demux_lavf.c, required a separate buffer allocation anyway
due to FFmpeg "idiosyncrasies".) This is the bulk of the demuxer_*
changes.

I'm not happy with this. There still isn't a good reason why there
should be a ring buffer, that is complex, and most of the time just
wastes half of the available memory. Maybe another rewrite soon.

It also contains bugs; you're an alpha tester now.
This commit is contained in:
wm4
2019-11-06 21:36:02 +01:00
parent abb089431d
commit f37f4de849
13 changed files with 314 additions and 175 deletions

View File

@@ -265,8 +265,9 @@ static int try_open_file(struct demuxer *demuxer, enum demux_check check)
struct stream *s = demuxer->stream;
if (check >= DEMUX_CHECK_UNSAFE) {
bstr d = stream_peek(s, PROBE_SIZE);
if (d.len < 1 || !mp_probe_cue(d))
char probe[PROBE_SIZE];
int len = stream_read_peek(s, probe, sizeof(probe));
if (len < 1 || !mp_probe_cue((bstr){probe, len}))
return -1;
}
struct priv *p = talloc_zero(demuxer, struct priv);

View File

@@ -321,7 +321,7 @@ static int d_open(demuxer_t *demuxer, enum demux_check check)
// Initialize the playback time. We need to read _some_ data to get the
// correct stream-layer time (at least with libdvdnav).
stream_peek(demuxer->stream, 1);
stream_read_peek(demuxer->stream, &(char){0}, 1);
reset_pts(demuxer);
p->slave = demux_open_url("-", &params, demuxer->cancel, demuxer->global);

View File

@@ -459,7 +459,9 @@ static int try_open_file(struct demuxer *demuxer, enum demux_check check)
return 0;
}
if (check >= DEMUX_CHECK_UNSAFE) {
if (!bstr_equals0(stream_peek(s, strlen(HEADER)), HEADER))
char header[sizeof(HEADER) - 1];
int len = stream_read_peek(s, header, sizeof(header));
if (len != strlen(HEADER) || memcmp(header, HEADER, len) != 0)
return -1;
}
p->data = stream_read_complete(s, demuxer, 1000000);

View File

@@ -458,11 +458,10 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
} else {
int nsize = av_clip(avpd.buf_size * 2, INITIAL_PROBE_SIZE,
PROBE_BUF_SIZE);
bstr buf = stream_peek(s, nsize);
if (buf.len <= avpd.buf_size)
nsize = stream_read_peek(s, avpd.buf, nsize);
if (nsize <= avpd.buf_size)
final_probe = true;
memcpy(avpd.buf, buf.start, buf.len);
avpd.buf_size = buf.len;
avpd.buf_size = nsize;
priv->avif = av_probe_input_format2(&avpd, avpd.buf_size > 0, &score);
}

View File

@@ -43,15 +43,17 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
probe_size *= 100;
}
bstr probe = stream_peek(demuxer->stream, probe_size);
if (probe.len == 0)
void *probe = ta_alloc_size(NULL, probe_size);
if (!probe)
return -1;
int probe_got = stream_read_peek(demuxer->stream, probe, probe_size);
struct stream *probe_stream =
stream_memory_open(demuxer->global, probe.start, probe.len);
stream_memory_open(demuxer->global, probe, probe_got);
struct mp_archive *mpa = mp_archive_new(mp_null_log, probe_stream, flags);
bool ok = !!mpa;
free_stream(probe_stream);
mp_archive_free(mpa);
ta_free(probe);
if (!ok)
return -1;

View File

@@ -1999,13 +1999,9 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
if (demuxer->params)
mkv_d->probably_webm_dash_init = demuxer->params->init_fragment.len > 0;
bstr start = stream_peek(s, 4);
uint32_t start_id = 0;
for (int n = 0; n < start.len; n++)
start_id = (start_id << 8) | start.start[n];
if (start_id != EBML_ID_EBML)
// Make sure you can seek back after read_ebml_header() if no EBML ID.
if (stream_read_peek(s, &(char[4]){0}, 4) != 4)
return -1;
if (!read_ebml_header(demuxer))
return -1;
MP_DBG(demuxer, "Found the head...\n");
@@ -2027,7 +2023,6 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
while (1) {
start_pos = stream_tell(s);
stream_peek(s, 4); // make sure we can always seek back
uint32_t id = ebml_read_id(s);
if (s->eof) {
if (!mkv_d->probably_webm_dash_init)
@@ -2836,7 +2831,6 @@ static int read_next_block_into_queue(demuxer_t *demuxer)
find_next_cluster:
mkv_d->cluster_end = 0;
for (;;) {
stream_peek(s, 4); // guarantee we can undo ebml_read_id() below
mkv_d->cluster_start = stream_tell(s);
uint32_t id = ebml_read_id(s);
if (id == MATROSKA_ID_CLUSTER)

View File

@@ -92,12 +92,13 @@ static int read_characters(stream_t *s, uint8_t *dst, int dstsize, int utf16)
}
return cur - dst;
} else {
bstr buf = stream_peek_buffer(s);
uint8_t *end = memchr(buf.start, '\n', buf.len);
int len = end ? end - buf.start + 1 : buf.len;
uint8_t buf[1024];
int buf_len = stream_read_peek(s, buf, sizeof(buf));
uint8_t *end = memchr(buf, '\n', buf_len);
int len = end ? end - buf + 1 : buf_len;
if (len > dstsize)
return -1; // line too long
memcpy(dst, buf.start, len);
memcpy(dst, buf, len);
stream_skip(s, len);
return len;
}
@@ -178,7 +179,9 @@ static int parse_m3u(struct pl_parser *p)
// Last resort: if the file extension is m3u, it might be headerless.
if (p->check_level == DEMUX_CHECK_UNSAFE) {
char *ext = mp_splitext(p->real_stream->url, NULL);
bstr data = stream_peek(p->real_stream, PROBE_SIZE);
char probe[PROBE_SIZE];
int len = stream_read_peek(p->real_stream, probe, sizeof(probe));
bstr data = {probe, len};
if (ext && data.len > 10 && maybe_text(data)) {
const char *exts[] = {"m3u", "m3u8", NULL};
for (int n = 0; exts[n]; n++) {
@@ -437,8 +440,9 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
p->real_stream = demuxer->stream;
p->add_base = true;
bstr probe_buf = stream_peek(demuxer->stream, PROBE_SIZE);
p->s = stream_memory_open(demuxer->global, probe_buf.start, probe_buf.len);
char probe[PROBE_SIZE];
int probe_len = stream_read_peek(p->real_stream, probe, sizeof(probe));
p->s = stream_memory_open(demuxer->global, probe, probe_len);
p->s->mime_type = demuxer->stream->mime_type;
p->utf16 = stream_skip_bom(p->s);
p->force = force;