[vlc-commits] stream: provide a generic stream_Peek() front-end
Rémi Denis-Courmont
git at videolan.org
Thu Jul 23 20:09:29 CEST 2015
vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Thu Jul 23 20:36:38 2015 +0300| [757ced87c874ecb08ad782b7e160b2ecc88cec1e] | committer: Rémi Denis-Courmont
stream: provide a generic stream_Peek() front-end
In most cases, there is no or little room for optimizing this, and
the benefits are minimal. This provides a generic implementation so
that each stream filter does not need to reinvent the wheel.
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=757ced87c874ecb08ad782b7e160b2ecc88cec1e
---
include/vlc_stream.h | 4 +-
src/input/stream.c | 183 +++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 162 insertions(+), 25 deletions(-)
diff --git a/include/vlc_stream.h b/include/vlc_stream.h
index 0856d7b..2b99d4a 100644
--- a/include/vlc_stream.h
+++ b/include/vlc_stream.h
@@ -117,8 +117,8 @@ enum stream_query_e
STREAM_GET_PRIVATE_ID_STATE, /* arg1=int i_private_data arg2=bool * res=can fail */
};
-VLC_API int stream_Read( stream_t *s, void *p_read, int i_read );
-VLC_API int stream_Peek( stream_t *s, const uint8_t **pp_peek, int i_peek );
+VLC_API ssize_t stream_Read(stream_t *, void *, size_t);
+VLC_API ssize_t stream_Peek(stream_t *, const uint8_t **, size_t);
VLC_API int stream_vaControl( stream_t *s, int i_query, va_list args );
VLC_API void stream_Delete( stream_t *s );
VLC_API int stream_Control( stream_t *s, int i_query, ... );
diff --git a/src/input/stream.c b/src/input/stream.c
index ef2a766..2d41523 100644
--- a/src/input/stream.c
+++ b/src/input/stream.c
@@ -31,6 +31,7 @@
#include <string.h>
#include <vlc_common.h>
+#include <vlc_block.h>
#include <vlc_memory.h>
#include <vlc_access.h>
#include <vlc_charset.h>
@@ -41,6 +42,7 @@
typedef struct stream_priv_t
{
stream_t stream;
+ block_t *peek;
/* UTF-16 and UTF-32 file reading */
struct {
@@ -63,6 +65,7 @@ stream_t *stream_CommonNew(vlc_object_t *parent)
s->psz_access = NULL;
s->psz_path = NULL;
+ priv->peek = NULL;
/* UTF16 and UTF32 text file conversion */
priv->text.conv = (vlc_iconv_t)(-1);
@@ -82,6 +85,9 @@ void stream_CommonDelete( stream_t *s )
if (priv->text.conv != (vlc_iconv_t)(-1))
vlc_iconv_close(priv->text.conv);
+ if (priv->peek != NULL)
+ block_Release(priv->peek);
+
free( s->psz_access );
free( s->psz_path );
vlc_object_release( s );
@@ -299,34 +305,135 @@ error:
}
/**
- * Try to read "i_read" bytes into a buffer pointed by "p_read". If
- * "p_read" is NULL then data are skipped instead of read.
- * \return The real number of bytes read/skip. If this value is less
- * than i_read that means that it's the end of the stream.
- * \note stream_Read increments the stream position, and when p_read is NULL,
- * this is its only task.
+ * Reads data from a byte stream.
+ *
+ * This function always waits for the requested number of bytes, unless a fatal
+ * error is encountered or the end-of-stream is reached first.
+ *
+ * If the buffer is NULL, data is skipped instead of read. This is effectively
+ * a relative forward seek, but it works even on non-seekable streams.
+ *
+ * \param buf start of buffer to read data into [OUT]
+ * \param len number of bytes to read
+ * \return the number of bytes read or a negative value on error.
*/
-int stream_Read( stream_t *s, void *p_read, int i_read )
+ssize_t stream_Read(stream_t *s, void *buf, size_t len)
{
- return s->pf_read( s, p_read, i_read );
+ stream_priv_t *priv = (stream_priv_t *)s;
+ block_t *peek = priv->peek;
+ size_t copy = 0;
+
+ if (unlikely(len == 0))
+ return 0;
+
+ if (peek != NULL)
+ {
+ copy = peek->i_buffer < len ? peek->i_buffer : len;
+
+ assert(copy > 0);
+ if (buf != NULL)
+ memcpy(buf, peek->p_buffer, copy);
+
+ peek->p_buffer += copy;
+ peek->i_buffer -= copy;
+ if (peek->i_buffer == 0)
+ {
+ block_Release(peek);
+ priv->peek = NULL;
+ }
+
+ if (buf != NULL)
+ buf = (unsigned char *)buf + copy;
+ len -= copy;
+ if (len == 0)
+ return copy;
+ }
+
+ ssize_t ret = s->pf_read(s, buf, len);
+ return (ret >= 0) ? (ssize_t)(ret + copy)
+ : ((copy > 0) ? (ssize_t)copy : ret);
}
/**
- * Store in pp_peek a pointer to the next "i_peek" bytes in the stream
- * \return The real number of valid bytes. If it's less
- * or equal to 0, *pp_peek is invalid.
- * \note pp_peek is a pointer to internal buffer and it will be invalid as
- * soons as other stream_* functions are called.
- * \note Contrary to stream_Read, stream_Peek doesn't modify the stream
- * position, and doesn't necessarily involve copying of data. It's mainly
- * used by the modules to quickly probe the (head of the) stream.
- * \note Due to input limitation, the return value could be less than i_peek
- * without meaning the end of the stream (but only when you have i_peek >=
- * p_input->i_bufsize)
+ * Peeks at data from a byte stream.
+ *
+ * This function buffers for the requested number of bytes, waiting if
+ * necessary. Then it stores a pointer to the buffer. Unlike stream_Read()
+ * or stream_Block(), this function does not modify the stream read offset.
+ *
+ * \note
+ * The buffer remains valid until the next read/peek or seek operation on the
+ * same stream. In case of error, the buffer address is undefined.
+ *
+ * \param bufp storage space for the buffer address [OUT]
+ * \param len number of bytes to peek
+ * \return the number of bytes actually available (shorter than requested if
+ * the end-of-stream is reached), or a negative value on error.
*/
-int stream_Peek( stream_t *s, const uint8_t **pp_peek, int i_peek )
+ssize_t stream_Peek(stream_t *s, const uint8_t **restrict bufp, size_t len)
{
- return s->pf_peek( s, pp_peek, i_peek );
+ stream_priv_t *priv = (stream_priv_t *)s;
+ block_t *peek = priv->peek;
+
+ if (peek == NULL)
+ {
+ peek = block_Alloc(len);
+ if (unlikely(peek == NULL))
+ return VLC_ENOMEM;
+
+ if (unlikely(len == 0))
+ {
+ *bufp = peek->p_buffer;
+ return 0;
+ }
+
+ ssize_t ret = s->pf_read(s, peek->p_buffer, len);
+ if (ret < 0)
+ {
+ block_Release(peek);
+ return ret;
+ }
+
+ *bufp = peek->p_buffer;
+ peek->i_buffer = ret;
+ priv->peek = peek;
+ return ret;
+ }
+
+ if (peek->i_buffer < len)
+ {
+ size_t avail = peek->i_buffer;
+
+ peek = block_Realloc(peek, 0, len);
+ priv->peek = peek;
+ if (unlikely(peek == NULL))
+ {
+ s->b_error = true; /* unrecoverable error */
+ return VLC_ENOMEM;
+ }
+ peek->i_buffer = avail;
+
+ ssize_t ret = s->pf_read(s, peek->p_buffer + avail, len - avail);
+ *bufp = peek->p_buffer;
+ if (ret >= 0)
+ peek->i_buffer += ret;
+ return peek->i_buffer;
+ }
+
+ /* Nothing to do */
+ *bufp = peek->p_buffer;
+ return len;
+}
+
+static int stream_ControlInternal(stream_t *s, int cmd, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, cmd);
+ ret = s->pf_control(s, cmd, ap);
+ va_end(ap);
+ return ret;
}
/**
@@ -334,9 +441,39 @@ int stream_Peek( stream_t *s, const uint8_t **pp_peek, int i_peek )
* possible "i_query" value and format arguments. Return VLC_SUCCESS
* if ... succeed ;) and VLC_EGENERIC if failed or unimplemented
*/
-int stream_vaControl( stream_t *s, int i_query, va_list args )
+int stream_vaControl(stream_t *s, int cmd, va_list args)
{
- return s->pf_control( s, i_query, args );
+ stream_priv_t *priv = (stream_priv_t *)s;
+
+ switch (cmd)
+ {
+ case STREAM_GET_POSITION:
+ {
+ uint64_t *ppos = va_arg(args, uint64_t *);
+
+ stream_ControlInternal(s, STREAM_GET_POSITION, ppos);
+ if (priv->peek != NULL)
+ {
+ assert(priv->peek->i_buffer <= *ppos);
+ *ppos -= priv->peek->i_buffer;
+ }
+ return VLC_SUCCESS;
+ }
+
+ case STREAM_SET_POSITION:
+ {
+ uint64_t pos = va_arg(args, uint64_t);
+
+ if (priv->peek != NULL)
+ {
+ block_Release(priv->peek);
+ priv->peek = NULL;
+ }
+
+ return stream_ControlInternal(s, STREAM_SET_POSITION, pos);
+ }
+ }
+ return s->pf_control(s, cmd, args);
}
/**
More information about the vlc-commits
mailing list