stream: add stream_unread_buffer()

demux_lavf probes up to 2 MB of data in the worst case. When the ffmpeg
demuxer is actually opened, the stream is seeked back to 0, and the
previously read data is thrown away.

This wasn't a problem for playback of local files, but it's less than
ideal for playing from slow media (like web streams), and breaks
completely if the media is not seekable (pipes, some web streams).

This new function is intended to allow fixing this. demux_lavf will use
it to put the read probe data back into the buffer.

The simplest way of implementing this function is by making it
transparently extend the normal stream buffer. This makes sure no
existing code is broken by new weird special cases. For simplicity
and to avoid possible performance loss due to extra dereferencing
when accessing the buffer, we just extend the static buffer from
8 KB to 2 MB. Normally, most of these 2 MB will stay uncommitted, so
there's no associated waste of memory. If demux_lavf really reads all
2 MB, the memory will be committed and stay unused, though.
This commit is contained in:
wm4 2013-05-24 23:20:09 +02:00
parent 7c4202b863
commit d5ad9a8f60
2 changed files with 33 additions and 4 deletions

View File

@ -52,6 +52,9 @@
#include "core/m_option.h"
#include "core/m_struct.h"
// Includes additional padding in case sizes get rounded up by sector size.
#define TOTAL_BUFFER_SIZE (STREAM_MAX_BUFFER_SIZE + STREAM_MAX_SECTOR_SIZE)
/// We keep these 2 for the gui atm, but they will be removed.
char *cdrom_device = NULL;
char *dvd_device = NULL;
@ -352,6 +355,7 @@ void stream_capture_write(stream_t *s)
int stream_read_unbuffered(stream_t *s, void *buf, int len)
{
int orig_len = len;
s->buf_pos = s->buf_len = 0;
// we will retry even if we already reached EOF previously.
switch (s->type) {
case STREAMTYPE_STREAM:
@ -396,6 +400,26 @@ eof_out:
return len;
}
// This works like stdio's ungetc(), but for more than one byte. Rewind the
// file position by buffer_size, and make all future reads/buffer fills read
// from the given buffer, until the buffer is exhausted or a seek outside of
// the buffer happens.
// You can unread at most STREAM_MAX_BUFFER_SIZE bytes.
void stream_unread_buffer(stream_t *s, void *buffer, size_t buffer_size)
{
assert(stream_tell(s) >= buffer_size); // can't unread to before file start
assert(buffer_size <= STREAM_MAX_BUFFER_SIZE);
// Need to include the remaining buffer to ensure no data is lost.
int remainder = s->buf_len - s->buf_pos;
// Successive buffer unreading might trigger this.
assert(buffer_size + remainder <= TOTAL_BUFFER_SIZE);
memmove(&s->buffer[buffer_size], &s->buffer[s->buf_pos], remainder);
memcpy(s->buffer, buffer, buffer_size);
s->buf_pos = 0;
s->buf_len = buffer_size + remainder;
s->eof = 0;
}
int stream_fill_buffer(stream_t *s)
{
int len = stream_read_unbuffered(s, s->buffer, STREAM_BUFFER_SIZE);
@ -625,7 +649,8 @@ void stream_update_size(stream_t *s)
static stream_t *new_stream(void)
{
stream_t *s = talloc_zero(NULL, stream_t);
stream_t *s = talloc_size(NULL, sizeof(stream_t) + TOTAL_BUFFER_SIZE);
memset(s, 0, sizeof(stream_t));
#if HAVE_WINSOCK2_H
{

View File

@ -58,6 +58,9 @@
#define STREAM_BUFFER_SIZE 2048
#define STREAM_MAX_SECTOR_SIZE (8 * 1024)
// Max buffer for initial probe.
#define STREAM_MAX_BUFFER_SIZE (2 * 1024 * 1024)
/// atm it will always use mode == STREAM_READ
/// streams that use the new api should check the mode at open
#define STREAM_READ 0
@ -184,14 +187,14 @@ typedef struct stream {
char *lavf_type; // name of expected demuxer type for lavf
struct MPOpts *opts;
streaming_ctrl_t *streaming_ctrl;
unsigned char buffer[STREAM_BUFFER_SIZE >
STREAM_MAX_SECTOR_SIZE ? STREAM_BUFFER_SIZE :
STREAM_MAX_SECTOR_SIZE];
FILE *capture_file;
char *capture_filename;
struct stream *uncached_stream;
// Includes additional padding in case sizes get rounded up by sector size.
unsigned char buffer[];
} stream_t;
#ifdef CONFIG_NETWORKING
@ -199,6 +202,7 @@ typedef struct stream {
#endif
int stream_fill_buffer(stream_t *s);
void stream_unread_buffer(stream_t *s, void *buffer, size_t buffer_size);
void stream_set_capture_file(stream_t *s, const char *filename);
void stream_capture_write(stream_t *s);