GST_TYPE_BASE_SRC);
static void gst_gio_base_src_finalize (GObject * object);
+
static gboolean gst_gio_base_src_start (GstBaseSrc * base_src);
+
static gboolean gst_gio_base_src_stop (GstBaseSrc * base_src);
+
static gboolean gst_gio_base_src_get_size (GstBaseSrc * base_src,
guint64 * size);
static gboolean gst_gio_base_src_is_seekable (GstBaseSrc * base_src);
+
static gboolean gst_gio_base_src_unlock (GstBaseSrc * base_src);
+
static gboolean gst_gio_base_src_unlock_stop (GstBaseSrc * base_src);
+
static gboolean gst_gio_base_src_check_get_range (GstBaseSrc * base_src);
+
static GstFlowReturn gst_gio_base_src_create (GstBaseSrc * base_src,
guint64 offset, guint size, GstBuffer ** buf);
gst_gio_base_src_class_init (GstGioBaseSrcClass * klass)
{
GObjectClass *gobject_class;
+
GstElementClass *gstelement_class;
+
GstBaseSrcClass *gstbasesrc_class;
gobject_class = (GObjectClass *) klass;
src->stream = NULL;
}
+ if (src->cache) {
+ gst_buffer_unref (src->cache);
+ src->cache = NULL;
+ }
+
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}
gst_gio_base_src_stop (GstBaseSrc * base_src)
{
GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+
gboolean success;
+
GError *err = NULL;
if (G_IS_INPUT_STREAM (src->stream)) {
if (G_IS_FILE_INPUT_STREAM (src->stream)) {
GFileInfo *info;
+
GError *err = NULL;
info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (src->stream),
if (GST_GIO_STREAM_IS_SEEKABLE (src->stream)) {
goffset old;
+
goffset stream_size;
+
gboolean ret;
+
GSeekable *seekable = G_SEEKABLE (src->stream);
+
GError *err = NULL;
old = g_seekable_tell (seekable);
gst_gio_base_src_is_seekable (GstBaseSrc * base_src)
{
GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+
gboolean seekable;
seekable = GST_GIO_STREAM_IS_SEEKABLE (src->stream);
GstBuffer ** buf_return)
{
GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+
GstBuffer *buf;
- gssize read;
- gboolean success, eos;
+
GstFlowReturn ret = GST_FLOW_OK;
- GError *err = NULL;
g_return_val_if_fail (G_IS_INPUT_STREAM (src->stream), GST_FLOW_ERROR);
- if (G_UNLIKELY (offset != src->position)) {
- if (!GST_GIO_STREAM_IS_SEEKABLE (src->stream))
- return GST_FLOW_NOT_SUPPORTED;
+ /* If we have the requested part in our cache take a subbuffer of that,
+ * otherwise fill the cache again with at least 4096 bytes from the
+ * requested offset and return a subbuffer of that.
+ *
+ * We need caching because every read/seek operation will need to go
+ * over DBus if our backend is GVfs and this is painfully slow. */
+ if (src->cache && offset >= GST_BUFFER_OFFSET (src->cache) &&
+ offset + size <= GST_BUFFER_OFFSET_END (src->cache)) {
- ret = gst_gio_seek (src, G_SEEKABLE (src->stream), offset, src->cancel);
+ GST_DEBUG_OBJECT (src, "Creating subbuffer from cached buffer: offset %"
+ G_GUINT64_FORMAT " length %u", offset, size);
- if (ret == GST_FLOW_OK)
- src->position = offset;
- else
- return ret;
- }
+ buf = gst_buffer_create_sub (src->cache,
+ offset - GST_BUFFER_OFFSET (src->cache), size);
- buf = gst_buffer_new_and_alloc (size);
+ GST_BUFFER_OFFSET (buf) = offset;
+ GST_BUFFER_OFFSET_END (buf) = offset + size;
+ GST_BUFFER_SIZE (buf) = size;
+ } else {
+ guint cachesize = MAX (4096, size);
- GST_LOG_OBJECT (src, "reading %u bytes from offset %" G_GUINT64_FORMAT,
- size, offset);
+ gssize read, res;
- read =
- g_input_stream_read (G_INPUT_STREAM (src->stream), GST_BUFFER_DATA (buf),
- size, src->cancel, &err);
+ gboolean success, eos;
- success = (read >= 0);
- eos = (size > 0 && read == 0);
+ GError *err = NULL;
- if (!success && !gst_gio_error (src, "g_input_stream_read", &err, &ret)) {
- GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
- ("Could not read from stream: %s", err->message));
- g_clear_error (&err);
- }
+ if (src->cache) {
+ gst_buffer_unref (src->cache);
+ src->cache = NULL;
+ }
- if (success && !eos) {
- src->position += read;
- GST_BUFFER_OFFSET (buf) = offset;
- GST_BUFFER_SIZE (buf) = read;
- *buf_return = buf;
- } else {
- /* !success || eos */
- gst_buffer_unref (buf);
+ if (G_UNLIKELY (offset != src->position)) {
+ if (!GST_GIO_STREAM_IS_SEEKABLE (src->stream))
+ return GST_FLOW_NOT_SUPPORTED;
+
+ GST_DEBUG_OBJECT (src, "Seeking to position %" G_GUINT64_FORMAT, offset);
+ ret = gst_gio_seek (src, G_SEEKABLE (src->stream), offset, src->cancel);
+
+ if (ret == GST_FLOW_OK)
+ src->position = offset;
+ else
+ return ret;
+ }
+
+ src->cache = gst_buffer_new_and_alloc (cachesize);
+
+ GST_LOG_OBJECT (src, "Reading %u bytes from offset %" G_GUINT64_FORMAT,
+ cachesize, offset);
+
+ /* GIO sometimes gives less bytes than requested although
+ * it's not at the end of file. SMB for example only
+ * supports reads up to 64k. So we loop here until we get at
+ * at least the requested amount of bytes or a read returns
+ * nothing. */
+ read = 0;
+ while (size - read > 0 && (res =
+ g_input_stream_read (G_INPUT_STREAM (src->stream),
+ GST_BUFFER_DATA (src->cache) + read, cachesize - read,
+ src->cancel, &err)) > 0) {
+ read += res;
+ }
+
+ success = (read >= 0);
+ eos = (cachesize > 0 && read == 0);
+
+ if (!success && !gst_gio_error (src, "g_input_stream_read", &err, &ret)) {
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
+ ("Could not read from stream: %s", err->message));
+ g_clear_error (&err);
+ }
+
+ if (success && !eos) {
+ src->position += read;
+ GST_BUFFER_SIZE (src->cache) = read;
+
+ GST_BUFFER_OFFSET (src->cache) = offset;
+ GST_BUFFER_OFFSET_END (src->cache) = offset + read;
+
+ GST_DEBUG_OBJECT (src, "Read successful");
+ GST_DEBUG_OBJECT (src, "Creating subbuffer from new "
+ "cached buffer: offset %" G_GUINT64_FORMAT " length %u", offset,
+ size);
+
+ buf = gst_buffer_create_sub (src->cache, 0, MIN (size, read));
+
+ GST_BUFFER_OFFSET (buf) = offset;
+ GST_BUFFER_OFFSET_END (buf) = offset + MIN (size, read);
+ GST_BUFFER_SIZE (buf) = MIN (size, read);
+ } else {
+ GST_DEBUG_OBJECT (src, "Read not successful");
+ gst_buffer_unref (src->cache);
+ src->cache = NULL;
+ buf = NULL;
+ }
+
+ if (eos)
+ ret = GST_FLOW_UNEXPECTED;
}
- if (eos)
- ret = GST_FLOW_UNEXPECTED;
+ *buf_return = buf;
return ret;
}
gst_gio_base_src_set_stream (GstGioBaseSrc * src, GInputStream * stream)
{
gboolean success;
+
GError *err = NULL;
g_return_if_fail (G_IS_INPUT_STREAM (stream));
#endif
#include "gstgiosrc.h"
+#include <string.h>
GST_DEBUG_CATEGORY_STATIC (gst_gio_src_debug);
#define GST_CAT_DEFAULT gst_gio_src_debug
GST_TYPE_GIO_BASE_SRC, gst_gio_uri_handler_do_init);
static void gst_gio_src_finalize (GObject * object);
+
static void gst_gio_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_gio_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
+
static gboolean gst_gio_src_start (GstBaseSrc * base_src);
+static gboolean gst_gio_src_check_get_range (GstBaseSrc * base_src);
+
static void
gst_gio_src_base_init (gpointer gclass)
{
gst_gio_src_class_init (GstGioSrcClass * klass)
{
GObjectClass *gobject_class;
+
GstElementClass *gstelement_class;
+
GstBaseSrcClass *gstbasesrc_class;
gobject_class = (GObjectClass *) klass;
G_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gio_src_start);
+ gstbasesrc_class->check_get_range =
+ GST_DEBUG_FUNCPTR (gst_gio_src_check_get_range);
}
static void
}
static gboolean
+gst_gio_src_check_get_range (GstBaseSrc * base_src)
+{
+ GstGioSrc *src = GST_GIO_SRC (base_src);
+
+ gchar *scheme;
+
+ if (src->file == NULL)
+ goto done;
+
+ scheme = g_file_get_uri_scheme (src->file);
+ if (scheme == NULL)
+ goto done;
+
+ if (strcmp (scheme, "file") == 0) {
+ GST_LOG_OBJECT (src, "local URI, assuming random access is possible");
+ g_free (scheme);
+ return TRUE;
+ } else if (strcmp (scheme, "http") == 0 || strcmp (scheme, "https") == 0) {
+ GST_LOG_OBJECT (src, "blacklisted protocol '%s', "
+ "no random access possible", scheme);
+ g_free (scheme);
+ return FALSE;
+ }
+
+ g_free (scheme);
+
+done:
+
+ GST_DEBUG_OBJECT (src, "undecided about random access, asking base class");
+
+ return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS,
+ check_get_range, (base_src), FALSE);
+}
+
+
+static gboolean
gst_gio_src_start (GstBaseSrc * base_src)
{
GstGioSrc *src = GST_GIO_SRC (base_src);
+
GError *err = NULL;
+
GInputStream *stream;
+
GCancellable *cancel = GST_GIO_BASE_SRC (src)->cancel;
+
gchar *uri = NULL;
if (src->file == NULL) {