mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
Add new downloadbuffer element
See https://bugzilla.gnome.org/show_bug.cgi?id=680183
This commit is contained in:
parent
35a1822d7a
commit
45855e2795
9 changed files with 2728 additions and 1 deletions
|
@ -11,6 +11,7 @@ libgstbase_@GST_API_VERSION@_la_SOURCES = \
|
||||||
gstbitreader.c \
|
gstbitreader.c \
|
||||||
gstbytereader.c \
|
gstbytereader.c \
|
||||||
gstbytewriter.c \
|
gstbytewriter.c \
|
||||||
|
gstsparsefile.c \
|
||||||
gstcollectpads.c \
|
gstcollectpads.c \
|
||||||
gstdataqueue.c \
|
gstdataqueue.c \
|
||||||
gstpushsrc.c \
|
gstpushsrc.c \
|
||||||
|
@ -18,7 +19,7 @@ libgstbase_@GST_API_VERSION@_la_SOURCES = \
|
||||||
gsttypefindhelper.c
|
gsttypefindhelper.c
|
||||||
|
|
||||||
libgstbase_@GST_API_VERSION@_la_CFLAGS = $(GST_OBJ_CFLAGS)
|
libgstbase_@GST_API_VERSION@_la_CFLAGS = $(GST_OBJ_CFLAGS)
|
||||||
libgstbase_@GST_API_VERSION@_la_LIBADD = $(GST_OBJ_LIBS)
|
libgstbase_@GST_API_VERSION@_la_LIBADD = $(GST_OBJ_LIBS) $(GIO_LIBS)
|
||||||
libgstbase_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
|
libgstbase_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
|
||||||
|
|
||||||
libgstbase_@GST_API_VERSION@includedir = \
|
libgstbase_@GST_API_VERSION@includedir = \
|
||||||
|
@ -34,6 +35,7 @@ libgstbase_@GST_API_VERSION@include_HEADERS = \
|
||||||
gstbitreader.h \
|
gstbitreader.h \
|
||||||
gstbytereader.h \
|
gstbytereader.h \
|
||||||
gstbytewriter.h \
|
gstbytewriter.h \
|
||||||
|
gstsparsefile.h \
|
||||||
gstcollectpads.h \
|
gstcollectpads.h \
|
||||||
gstdataqueue.h \
|
gstdataqueue.h \
|
||||||
gstpushsrc.h \
|
gstpushsrc.h \
|
||||||
|
|
454
libs/gst/base/gstsparsefile.c
Normal file
454
libs/gst/base/gstsparsefile.c
Normal file
|
@ -0,0 +1,454 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2014 Wim Taymans <wtaymans@redhat.com>
|
||||||
|
*
|
||||||
|
* gstcahcefile.c:
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include "gstsparsefile.h"
|
||||||
|
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
#include <io.h> /* lseek, open, close, read */
|
||||||
|
#undef lseek
|
||||||
|
#define lseek _lseeki64
|
||||||
|
#undef off_t
|
||||||
|
#define off_t guint64
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FSEEKO
|
||||||
|
#define FSEEK_FILE(file,offset) (fseeko (file, (off_t) offset, SEEK_SET) != 0)
|
||||||
|
#elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
|
||||||
|
#define FSEEK_FILE(file,offset) (lseek (fileno (file), (off_t) offset, SEEK_SET) == (off_t) -1)
|
||||||
|
#else
|
||||||
|
#define FSEEK_FILE(file,offset) (fseek (file, offset, SEEK_SET) != 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _GstSparseRange GstSparseRange;
|
||||||
|
|
||||||
|
struct _GstSparseRange
|
||||||
|
{
|
||||||
|
GstSparseRange *next;
|
||||||
|
|
||||||
|
gsize start;
|
||||||
|
gsize stop;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RANGE_CONTAINS(r,o) ((r)->start <= (o) && (r)->stop > (o))
|
||||||
|
|
||||||
|
struct _GstSparseFile
|
||||||
|
{
|
||||||
|
gint fd;
|
||||||
|
FILE *file;
|
||||||
|
gsize current_pos;
|
||||||
|
|
||||||
|
GstSparseRange *ranges;
|
||||||
|
guint n_ranges;
|
||||||
|
|
||||||
|
GstSparseRange *write_range;
|
||||||
|
GstSparseRange *read_range;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GstSparseRange *
|
||||||
|
get_write_range (GstSparseFile * file, gsize offset)
|
||||||
|
{
|
||||||
|
GstSparseRange *next, *prev, *result = NULL;
|
||||||
|
|
||||||
|
if (file->write_range && file->write_range->stop == offset)
|
||||||
|
return file->write_range;
|
||||||
|
|
||||||
|
prev = NULL;
|
||||||
|
next = file->ranges;
|
||||||
|
while (next) {
|
||||||
|
if (next->start > offset)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (next->stop >= offset) {
|
||||||
|
result = next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = next;
|
||||||
|
next = next->next;
|
||||||
|
}
|
||||||
|
if (result == NULL) {
|
||||||
|
result = g_slice_new0 (GstSparseRange);
|
||||||
|
result->start = offset;
|
||||||
|
result->stop = offset;
|
||||||
|
|
||||||
|
result->next = next;
|
||||||
|
if (prev)
|
||||||
|
prev->next = result;
|
||||||
|
else
|
||||||
|
file->ranges = result;
|
||||||
|
|
||||||
|
file->write_range = result;
|
||||||
|
file->read_range = NULL;
|
||||||
|
|
||||||
|
file->n_ranges++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstSparseRange *
|
||||||
|
get_read_range (GstSparseFile * file, gsize offset, gsize count)
|
||||||
|
{
|
||||||
|
GstSparseRange *walk, *result = NULL;
|
||||||
|
|
||||||
|
if (file->read_range && RANGE_CONTAINS (file->read_range, offset))
|
||||||
|
return file->read_range;
|
||||||
|
|
||||||
|
for (walk = file->ranges; walk; walk = walk->next) {
|
||||||
|
if (walk->start > offset)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (walk->stop >= offset + count) {
|
||||||
|
result = walk;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_sparse_file_new:
|
||||||
|
*
|
||||||
|
* Make a new #GstSparseFile backed by the file represented with @fd.
|
||||||
|
*
|
||||||
|
* Returns: a new #GstSparseFile, gst_sparse_file_free() after usage.
|
||||||
|
*/
|
||||||
|
GstSparseFile *
|
||||||
|
gst_sparse_file_new (void)
|
||||||
|
{
|
||||||
|
GstSparseFile *result;
|
||||||
|
|
||||||
|
result = g_slice_new0 (GstSparseFile);
|
||||||
|
result->current_pos = 0;
|
||||||
|
result->ranges = NULL;
|
||||||
|
result->n_ranges = 0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_sparse_file_set_fd:
|
||||||
|
* @file: a #GstSparseFile
|
||||||
|
* @fd: a file descriptor
|
||||||
|
*
|
||||||
|
* Store the data for @file in the file represented with @fd.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE when @fd could be set
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_sparse_file_set_fd (GstSparseFile * file, gint fd)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (file != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (fd != 0, FALSE);
|
||||||
|
|
||||||
|
file->file = fdopen (fd, "wb+");
|
||||||
|
file->fd = fd;
|
||||||
|
|
||||||
|
return file->file != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_sparse_file_clear:
|
||||||
|
* @file: a #GstSparseFile
|
||||||
|
*
|
||||||
|
* Clear all the ranges in @file.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_sparse_file_clear (GstSparseFile * file)
|
||||||
|
{
|
||||||
|
g_return_if_fail (file != NULL);
|
||||||
|
|
||||||
|
if (file->file) {
|
||||||
|
fclose (file->file);
|
||||||
|
file->file = fdopen (file->fd, "wb+");
|
||||||
|
}
|
||||||
|
g_slice_free_chain (GstSparseRange, file->ranges, next);
|
||||||
|
file->current_pos = 0;
|
||||||
|
file->ranges = NULL;
|
||||||
|
file->n_ranges = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_sparse_file_free:
|
||||||
|
* @file: a #GstSparseFile
|
||||||
|
*
|
||||||
|
* Free the memory used by @file.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_sparse_file_free (GstSparseFile * file)
|
||||||
|
{
|
||||||
|
g_return_if_fail (file != NULL);
|
||||||
|
|
||||||
|
if (file->file) {
|
||||||
|
fflush (file->file);
|
||||||
|
fclose (file->file);
|
||||||
|
}
|
||||||
|
g_slice_free_chain (GstSparseRange, file->ranges, next);
|
||||||
|
g_slice_free (GstSparseFile, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_sparse_file_write:
|
||||||
|
* @file: a #GstSparseFile
|
||||||
|
* @offset: the offset
|
||||||
|
* @data: the data
|
||||||
|
* @count: amount of bytes
|
||||||
|
* @available: amount of bytes already available
|
||||||
|
* @error: a #GError
|
||||||
|
*
|
||||||
|
* Write @count bytes from @data to @file at @offset.
|
||||||
|
*
|
||||||
|
* If @available is not %NULL, it will be updated with the amount of
|
||||||
|
* data already available after the last written byte.
|
||||||
|
*
|
||||||
|
* Returns: The number of bytes written of 0 on error.
|
||||||
|
*/
|
||||||
|
gsize
|
||||||
|
gst_sparse_file_write (GstSparseFile * file, gsize offset, gconstpointer data,
|
||||||
|
gsize count, gsize * available, GError ** error)
|
||||||
|
{
|
||||||
|
GstSparseRange *range, *next;
|
||||||
|
gsize stop;
|
||||||
|
|
||||||
|
g_return_val_if_fail (file != NULL, 0);
|
||||||
|
g_return_val_if_fail (count != 0, 0);
|
||||||
|
|
||||||
|
if (file->file) {
|
||||||
|
if (file->current_pos != offset) {
|
||||||
|
GST_DEBUG ("seeking to %" G_GSIZE_FORMAT, offset);
|
||||||
|
if (FSEEK_FILE (file->file, offset))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (fwrite (data, count, 1, file->file) != 1)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
file->current_pos = offset + count;
|
||||||
|
|
||||||
|
/* update the new stop position in the range */
|
||||||
|
range = get_write_range (file, offset);
|
||||||
|
stop = offset + count;
|
||||||
|
range->stop = MAX (range->stop, stop);
|
||||||
|
|
||||||
|
/* see if we can merge with next region */
|
||||||
|
while ((next = range->next)) {
|
||||||
|
if (next->start > range->stop)
|
||||||
|
break;
|
||||||
|
|
||||||
|
GST_DEBUG ("merging range %" G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT ", next %"
|
||||||
|
G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT, range->start, range->stop,
|
||||||
|
next->start, next->stop);
|
||||||
|
|
||||||
|
range->stop = MAX (next->stop, range->stop);
|
||||||
|
range->next = next->next;
|
||||||
|
|
||||||
|
if (file->write_range == next)
|
||||||
|
file->write_range = NULL;
|
||||||
|
if (file->read_range == next)
|
||||||
|
file->read_range = NULL;
|
||||||
|
g_slice_free (GstSparseRange, next);
|
||||||
|
file->n_ranges--;
|
||||||
|
}
|
||||||
|
if (available)
|
||||||
|
*available = range->stop - stop;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
error:
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
||||||
|
"Error writing file: %s", g_strerror (errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_sparse_file_read:
|
||||||
|
* @file: a #GstSparseFile
|
||||||
|
* @offset: the offset
|
||||||
|
* @data: the data
|
||||||
|
* @count: amount of bytes
|
||||||
|
* @remaining: amount of bytes remaining
|
||||||
|
* @error: a #GError
|
||||||
|
*
|
||||||
|
* Read @count bytes from @file at @offset into @data.
|
||||||
|
*
|
||||||
|
* On error, @error will be set. If there are no @count bytes available
|
||||||
|
* at @offset, %G_IO_ERROR_WOULD_BLOCK is returned.
|
||||||
|
*
|
||||||
|
* @remaining will be set to the amount of bytes remaining in the read
|
||||||
|
* range.
|
||||||
|
*
|
||||||
|
* Returns: The number of bytes read of 0 on error.
|
||||||
|
*/
|
||||||
|
gsize
|
||||||
|
gst_sparse_file_read (GstSparseFile * file, gsize offset, gpointer data,
|
||||||
|
gsize count, gsize * remaining, GError ** error)
|
||||||
|
{
|
||||||
|
GstSparseRange *range;
|
||||||
|
gsize res = 0;
|
||||||
|
|
||||||
|
g_return_val_if_fail (file != NULL, 0);
|
||||||
|
g_return_val_if_fail (count != 0, 0);
|
||||||
|
|
||||||
|
if ((range = get_read_range (file, offset, count)) == NULL)
|
||||||
|
goto no_range;
|
||||||
|
|
||||||
|
if (file->file) {
|
||||||
|
if (file->current_pos != offset) {
|
||||||
|
GST_DEBUG ("seeking from %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT,
|
||||||
|
file->current_pos, offset);
|
||||||
|
if (FSEEK_FILE (file->file, offset))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
res = fread (data, 1, count, file->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
file->current_pos = offset + res;
|
||||||
|
|
||||||
|
if (G_UNLIKELY (res < count))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (remaining)
|
||||||
|
*remaining = range->stop - file->current_pos;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
no_range:
|
||||||
|
{
|
||||||
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
|
||||||
|
"Offset not written to file yet");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
error:
|
||||||
|
{
|
||||||
|
if (ferror (file->file)) {
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
||||||
|
"Error reading file: %s", g_strerror (errno));
|
||||||
|
} else if (feof (file->file)) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_sparse_file_n_ranges:
|
||||||
|
* @file: a #GstSparseFile
|
||||||
|
*
|
||||||
|
* Get the number of ranges that are written in @file.
|
||||||
|
*
|
||||||
|
* Returns: the number of written ranges.
|
||||||
|
*/
|
||||||
|
guint
|
||||||
|
gst_sparse_file_n_ranges (GstSparseFile * file)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (file != NULL, 0);
|
||||||
|
|
||||||
|
return file->n_ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_sparse_file_get_range_before:
|
||||||
|
* @file: a #GstSparseFile
|
||||||
|
* @offset: the range offset
|
||||||
|
* @start: result start
|
||||||
|
* @stop: result stop
|
||||||
|
*
|
||||||
|
* Get the start and stop offset of the range containing data before or
|
||||||
|
* including @offset.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the range with data before @offset exists.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_sparse_file_get_range_before (GstSparseFile * file, gsize offset,
|
||||||
|
gsize * start, gsize * stop)
|
||||||
|
{
|
||||||
|
GstSparseRange *walk, *result = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (file != NULL, FALSE);
|
||||||
|
|
||||||
|
for (walk = file->ranges; walk; walk = walk->next) {
|
||||||
|
GST_DEBUG ("start %" G_GSIZE_FORMAT " > %" G_GSIZE_FORMAT,
|
||||||
|
walk->stop, offset);
|
||||||
|
if (walk->start > offset)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (walk->start <= offset)
|
||||||
|
result = walk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
if (start)
|
||||||
|
*start = result->start;
|
||||||
|
if (stop)
|
||||||
|
*stop = result->stop;
|
||||||
|
}
|
||||||
|
return result != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_sparse_file_get_range_after:
|
||||||
|
* @file: a #GstSparseFile
|
||||||
|
* @offset: the range offset
|
||||||
|
* @start: result start
|
||||||
|
* @stop: result stop
|
||||||
|
*
|
||||||
|
* Get the start and stop offset of the range containing data after or
|
||||||
|
* including @offset.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the range with data after @offset exists.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_sparse_file_get_range_after (GstSparseFile * file, gsize offset,
|
||||||
|
gsize * start, gsize * stop)
|
||||||
|
{
|
||||||
|
GstSparseRange *walk, *result = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (file != NULL, FALSE);
|
||||||
|
|
||||||
|
for (walk = file->ranges; walk; walk = walk->next) {
|
||||||
|
GST_DEBUG ("stop %" G_GSIZE_FORMAT " > %" G_GSIZE_FORMAT,
|
||||||
|
walk->stop, offset);
|
||||||
|
if (walk->stop > offset) {
|
||||||
|
result = walk;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
if (start)
|
||||||
|
*start = result->start;
|
||||||
|
if (stop)
|
||||||
|
*stop = result->stop;
|
||||||
|
}
|
||||||
|
return result != NULL;
|
||||||
|
}
|
57
libs/gst/base/gstsparsefile.h
Normal file
57
libs/gst/base/gstsparsefile.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2014 Wim Taymans <wtaymans@redhat.com>
|
||||||
|
*
|
||||||
|
* gstcahcefile.h:
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#ifndef __GST_SPARSE_FILE_H__
|
||||||
|
#define __GST_SPARSE_FILE_H__
|
||||||
|
|
||||||
|
typedef struct _GstSparseFile GstSparseFile;
|
||||||
|
|
||||||
|
GstSparseFile * gst_sparse_file_new (void);
|
||||||
|
void gst_sparse_file_free (GstSparseFile *file);
|
||||||
|
|
||||||
|
gboolean gst_sparse_file_set_fd (GstSparseFile *file, gint fd);
|
||||||
|
void gst_sparse_file_clear (GstSparseFile *file);
|
||||||
|
|
||||||
|
gsize gst_sparse_file_write (GstSparseFile *file,
|
||||||
|
gsize offset,
|
||||||
|
gconstpointer data,
|
||||||
|
gsize count,
|
||||||
|
gsize *available,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gsize gst_sparse_file_read (GstSparseFile *file,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data,
|
||||||
|
gsize count,
|
||||||
|
gsize *remaining,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
guint gst_sparse_file_n_ranges (GstSparseFile *file);
|
||||||
|
gboolean gst_sparse_file_get_range_before (GstSparseFile *file, gsize offset,
|
||||||
|
gsize *start, gsize *stop);
|
||||||
|
gboolean gst_sparse_file_get_range_after (GstSparseFile *file, gsize offset,
|
||||||
|
gsize *start, gsize *stop);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __GST_SPARSE_FILE_H__ */
|
|
@ -4,6 +4,7 @@ plugin_LTLIBRARIES = libgstcoreelements.la
|
||||||
libgstcoreelements_la_DEPENDENCIES = $(top_builddir)/gst/libgstreamer-@GST_API_VERSION@.la
|
libgstcoreelements_la_DEPENDENCIES = $(top_builddir)/gst/libgstreamer-@GST_API_VERSION@.la
|
||||||
libgstcoreelements_la_SOURCES = \
|
libgstcoreelements_la_SOURCES = \
|
||||||
gstcapsfilter.c \
|
gstcapsfilter.c \
|
||||||
|
gstdownloadbuffer.c \
|
||||||
gstelements.c \
|
gstelements.c \
|
||||||
gstfakesrc.c \
|
gstfakesrc.c \
|
||||||
gstfakesink.c \
|
gstfakesink.c \
|
||||||
|
@ -31,6 +32,7 @@ libgstcoreelements_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
gstcapsfilter.h \
|
gstcapsfilter.h \
|
||||||
|
gstdownloadbuffer.h \
|
||||||
gstfakesink.h \
|
gstfakesink.h \
|
||||||
gstfakesrc.h \
|
gstfakesrc.h \
|
||||||
gstfdsrc.h \
|
gstfdsrc.h \
|
||||||
|
|
1852
plugins/elements/gstdownloadbuffer.c
Normal file
1852
plugins/elements/gstdownloadbuffer.c
Normal file
File diff suppressed because it is too large
Load diff
128
plugins/elements/gstdownloadbuffer.h
Normal file
128
plugins/elements/gstdownloadbuffer.h
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2014 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* gstdownloadbuffer.h:
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#ifndef __GST_DOWNLOAD_BUFFER_H__
|
||||||
|
#define __GST_DOWNLOAD_BUFFER_H__
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <gst/base/gstsparsefile.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_DOWNLOAD_BUFFER \
|
||||||
|
(gst_download_buffer_get_type())
|
||||||
|
#define GST_DOWNLOAD_BUFFER(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DOWNLOAD_BUFFER,GstDownloadBuffer))
|
||||||
|
#define GST_DOWNLOAD_BUFFER_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DOWNLOAD_BUFFER,GstDownloadBufferClass))
|
||||||
|
#define GST_IS_DOWNLOAD_BUFFER(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DOWNLOAD_BUFFER))
|
||||||
|
#define GST_IS_DOWNLOAD_BUFFER_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DOWNLOAD_BUFFER))
|
||||||
|
#define GST_DOWNLOAD_BUFFER_CAST(obj) \
|
||||||
|
((GstDownloadBuffer *)(obj))
|
||||||
|
|
||||||
|
typedef struct _GstDownloadBuffer GstDownloadBuffer;
|
||||||
|
typedef struct _GstDownloadBufferClass GstDownloadBufferClass;
|
||||||
|
typedef struct _GstDownloadBufferSize GstDownloadBufferSize;
|
||||||
|
|
||||||
|
/* used to keep track of sizes (current and max) */
|
||||||
|
struct _GstDownloadBufferSize
|
||||||
|
{
|
||||||
|
guint bytes;
|
||||||
|
guint64 time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstDownloadBuffer
|
||||||
|
{
|
||||||
|
GstElement element;
|
||||||
|
|
||||||
|
/*< private > */
|
||||||
|
GstPad *sinkpad;
|
||||||
|
GstPad *srcpad;
|
||||||
|
|
||||||
|
/* upstream size in bytes (if downstream is operating in pull mode) */
|
||||||
|
guint64 upstream_size;
|
||||||
|
|
||||||
|
/* flowreturn when srcpad is paused */
|
||||||
|
GstFlowReturn srcresult;
|
||||||
|
GstFlowReturn sinkresult;
|
||||||
|
gboolean unexpected;
|
||||||
|
|
||||||
|
/* the queue of data we're keeping our hands on */
|
||||||
|
GstSparseFile *file;
|
||||||
|
guint64 write_pos;
|
||||||
|
guint64 read_pos;
|
||||||
|
gboolean filling;
|
||||||
|
|
||||||
|
GstDownloadBufferSize cur_level;
|
||||||
|
GstDownloadBufferSize max_level;
|
||||||
|
gint low_percent; /* low/high watermarks for buffering */
|
||||||
|
gint high_percent;
|
||||||
|
|
||||||
|
/* current buffering state */
|
||||||
|
gboolean is_buffering;
|
||||||
|
gint buffering_percent;
|
||||||
|
|
||||||
|
/* for measuring input/output rates */
|
||||||
|
GTimer *in_timer;
|
||||||
|
gboolean in_timer_started;
|
||||||
|
gdouble last_in_elapsed;
|
||||||
|
guint64 bytes_in;
|
||||||
|
gdouble byte_in_rate;
|
||||||
|
gdouble byte_in_period;
|
||||||
|
|
||||||
|
GTimer *out_timer;
|
||||||
|
gboolean out_timer_started;
|
||||||
|
gdouble last_out_elapsed;
|
||||||
|
guint64 bytes_out;
|
||||||
|
gdouble byte_out_rate;
|
||||||
|
|
||||||
|
GMutex qlock; /* lock for queue (vs object lock) */
|
||||||
|
gboolean waiting_add;
|
||||||
|
GCond item_add; /* signals buffers now available for reading */
|
||||||
|
guint64 waiting_offset;
|
||||||
|
|
||||||
|
/* temp location stuff */
|
||||||
|
gchar *temp_template;
|
||||||
|
gboolean temp_location_set;
|
||||||
|
gchar *temp_location;
|
||||||
|
gboolean temp_remove;
|
||||||
|
gint temp_fd;
|
||||||
|
gboolean seeking;
|
||||||
|
|
||||||
|
GstEvent *stream_start_event;
|
||||||
|
GstEvent *segment_event;
|
||||||
|
|
||||||
|
volatile gint downstream_may_block;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstDownloadBufferClass
|
||||||
|
{
|
||||||
|
GstElementClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_GNUC_INTERNAL GType gst_download_buffer_get_type (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_DOWNLOAD_BUFFER_H__ */
|
|
@ -28,6 +28,7 @@
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
|
||||||
#include "gstcapsfilter.h"
|
#include "gstcapsfilter.h"
|
||||||
|
#include "gstdownloadbuffer.h"
|
||||||
#include "gstfakesink.h"
|
#include "gstfakesink.h"
|
||||||
#include "gstfakesrc.h"
|
#include "gstfakesrc.h"
|
||||||
#include "gstfdsrc.h"
|
#include "gstfdsrc.h"
|
||||||
|
@ -51,6 +52,9 @@ plugin_init (GstPlugin * plugin)
|
||||||
if (!gst_element_register (plugin, "capsfilter", GST_RANK_NONE,
|
if (!gst_element_register (plugin, "capsfilter", GST_RANK_NONE,
|
||||||
gst_capsfilter_get_type ()))
|
gst_capsfilter_get_type ()))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
if (!gst_element_register (plugin, "downloadbuffer", GST_RANK_NONE,
|
||||||
|
gst_download_buffer_get_type ()))
|
||||||
|
return FALSE;
|
||||||
if (!gst_element_register (plugin, "fakesrc", GST_RANK_NONE,
|
if (!gst_element_register (plugin, "fakesrc", GST_RANK_NONE,
|
||||||
gst_fake_src_get_type ()))
|
gst_fake_src_get_type ()))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
|
@ -91,6 +91,7 @@ REGISTRY_CHECKS = \
|
||||||
libs/baseparse \
|
libs/baseparse \
|
||||||
libs/basesrc \
|
libs/basesrc \
|
||||||
libs/basesink \
|
libs/basesink \
|
||||||
|
libs/sparsefile \
|
||||||
libs/controller \
|
libs/controller \
|
||||||
libs/queuearray \
|
libs/queuearray \
|
||||||
libs/typefindhelper \
|
libs/typefindhelper \
|
||||||
|
@ -142,6 +143,7 @@ check_PROGRAMS = \
|
||||||
libs/bitreader-noinline \
|
libs/bitreader-noinline \
|
||||||
libs/bytereader-noinline \
|
libs/bytereader-noinline \
|
||||||
libs/bytewriter-noinline \
|
libs/bytewriter-noinline \
|
||||||
|
libs/sparsefile \
|
||||||
libs/collectpads \
|
libs/collectpads \
|
||||||
libs/gstnetclientclock \
|
libs/gstnetclientclock \
|
||||||
libs/gstnettimeprovider \
|
libs/gstnettimeprovider \
|
||||||
|
|
226
tests/check/libs/sparsefile.c
Normal file
226
tests/check/libs/sparsefile.c
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* unit test for cachefile helper
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Wim Taymans <wtaymans@redhat.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
|
||||||
|
#include <gst/check/gstcheck.h>
|
||||||
|
|
||||||
|
#include <gst/base/gstsparsefile.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
expect_range_before (GstSparseFile * file, gsize offset, gsize start,
|
||||||
|
gsize stop)
|
||||||
|
{
|
||||||
|
gsize tstart, tstop;
|
||||||
|
|
||||||
|
fail_unless (gst_sparse_file_get_range_before (file, offset, &tstart,
|
||||||
|
&tstop) == TRUE);
|
||||||
|
fail_unless (tstart == start);
|
||||||
|
fail_unless (tstop == stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
expect_range_after (GstSparseFile * file, gsize offset, gsize start, gsize stop)
|
||||||
|
{
|
||||||
|
gsize tstart, tstop;
|
||||||
|
|
||||||
|
fail_unless (gst_sparse_file_get_range_after (file, offset, &tstart,
|
||||||
|
&tstop) == TRUE);
|
||||||
|
fail_unless (tstart == start);
|
||||||
|
fail_unless (tstop == stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
expect_write (GstSparseFile * file, gsize offset, gsize count, gsize result,
|
||||||
|
gsize avail)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
gchar buffer[200];
|
||||||
|
gsize res, a;
|
||||||
|
|
||||||
|
res = gst_sparse_file_write (file, offset, buffer, count, &a, &error);
|
||||||
|
if (res != result)
|
||||||
|
return FALSE;
|
||||||
|
if (res == 0) {
|
||||||
|
if (error == NULL)
|
||||||
|
return FALSE;
|
||||||
|
g_clear_error (&error);
|
||||||
|
} else if (a != avail)
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
expect_read (GstSparseFile * file, gsize offset, gsize count, gsize result,
|
||||||
|
gsize avail)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
gchar buffer[200];
|
||||||
|
gsize res, a;
|
||||||
|
|
||||||
|
res = gst_sparse_file_read (file, offset, buffer, count, &a, &error);
|
||||||
|
if (res != result)
|
||||||
|
return FALSE;
|
||||||
|
if (res == 0) {
|
||||||
|
if (error == NULL)
|
||||||
|
return FALSE;
|
||||||
|
g_clear_error (&error);
|
||||||
|
} else if (a != avail)
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_START_TEST (test_write_read)
|
||||||
|
{
|
||||||
|
GstSparseFile *file;
|
||||||
|
gint fd;
|
||||||
|
gchar *name;
|
||||||
|
gsize start, stop;
|
||||||
|
|
||||||
|
name = g_strdup ("cachefile-testXXXXXX");
|
||||||
|
fd = g_mkstemp (name);
|
||||||
|
fail_if (fd == -1);
|
||||||
|
|
||||||
|
file = gst_sparse_file_new ();
|
||||||
|
fail_unless (file != NULL);
|
||||||
|
fail_unless (gst_sparse_file_set_fd (file, fd));
|
||||||
|
fail_unless (gst_sparse_file_n_ranges (file) == 0);
|
||||||
|
|
||||||
|
/* should fail, we didn't write anything yet */
|
||||||
|
fail_unless (expect_read (file, 0, 100, 0, 0));
|
||||||
|
|
||||||
|
/* no ranges, searching for a range should fail */
|
||||||
|
fail_unless (gst_sparse_file_n_ranges (file) == 0);
|
||||||
|
fail_unless (gst_sparse_file_get_range_before (file, 0, &start,
|
||||||
|
&stop) == FALSE);
|
||||||
|
fail_unless (gst_sparse_file_get_range_before (file, 10, &start,
|
||||||
|
&stop) == FALSE);
|
||||||
|
fail_unless (gst_sparse_file_get_range_after (file, 0, &start,
|
||||||
|
&stop) == FALSE);
|
||||||
|
fail_unless (gst_sparse_file_get_range_after (file, 10, &start,
|
||||||
|
&stop) == FALSE);
|
||||||
|
|
||||||
|
/* now write some data */
|
||||||
|
fail_unless (expect_write (file, 0, 100, 100, 0));
|
||||||
|
|
||||||
|
/* we have 1 range now */
|
||||||
|
fail_unless (gst_sparse_file_n_ranges (file) == 1);
|
||||||
|
expect_range_before (file, 0, 0, 100);
|
||||||
|
expect_range_after (file, 0, 0, 100);
|
||||||
|
expect_range_before (file, 100, 0, 100);
|
||||||
|
expect_range_before (file, 50, 0, 100);
|
||||||
|
expect_range_before (file, 200, 0, 100);
|
||||||
|
fail_unless (gst_sparse_file_get_range_after (file, 100, &start,
|
||||||
|
&stop) == FALSE);
|
||||||
|
expect_range_after (file, 50, 0, 100);
|
||||||
|
|
||||||
|
/* we can read all data now */
|
||||||
|
fail_unless (expect_read (file, 0, 100, 100, 0));
|
||||||
|
/* we can read less */
|
||||||
|
fail_unless (expect_read (file, 0, 50, 50, 50));
|
||||||
|
/* but we can't read more than what is written */
|
||||||
|
fail_unless (expect_read (file, 0, 101, 0, 0));
|
||||||
|
|
||||||
|
g_unlink (name);
|
||||||
|
gst_sparse_file_free (file);
|
||||||
|
g_free (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (test_write_merge)
|
||||||
|
{
|
||||||
|
GstSparseFile *file;
|
||||||
|
gint fd;
|
||||||
|
gchar *name;
|
||||||
|
gsize start, stop;
|
||||||
|
|
||||||
|
name = g_strdup ("cachefile-testXXXXXX");
|
||||||
|
fd = g_mkstemp (name);
|
||||||
|
fail_if (fd == -1);
|
||||||
|
|
||||||
|
file = gst_sparse_file_new ();
|
||||||
|
gst_sparse_file_set_fd (file, fd);
|
||||||
|
|
||||||
|
/* write something at offset 0 */
|
||||||
|
fail_unless (expect_write (file, 0, 100, 100, 0));
|
||||||
|
/* we have 1 range now */
|
||||||
|
fail_unless (gst_sparse_file_n_ranges (file) == 1);
|
||||||
|
expect_range_before (file, 110, 0, 100);
|
||||||
|
expect_range_after (file, 50, 0, 100);
|
||||||
|
fail_unless (gst_sparse_file_get_range_after (file, 100, &start,
|
||||||
|
&stop) == FALSE);
|
||||||
|
|
||||||
|
/* read should fail */
|
||||||
|
fail_unless (expect_read (file, 50, 150, 0, 0));
|
||||||
|
|
||||||
|
/* write something at offset 150 */
|
||||||
|
fail_unless (expect_write (file, 150, 100, 100, 0));
|
||||||
|
/* we have 2 ranges now */
|
||||||
|
fail_unless (gst_sparse_file_n_ranges (file) == 2);
|
||||||
|
expect_range_before (file, 110, 0, 100);
|
||||||
|
expect_range_after (file, 50, 0, 100);
|
||||||
|
expect_range_after (file, 100, 150, 250);
|
||||||
|
expect_range_before (file, 150, 150, 250);
|
||||||
|
|
||||||
|
/* read should still fail */
|
||||||
|
fail_unless (expect_read (file, 50, 150, 0, 0));
|
||||||
|
|
||||||
|
/* fill the hole */
|
||||||
|
fail_unless (expect_write (file, 100, 50, 50, 100));
|
||||||
|
/* we have 1 range now */
|
||||||
|
fail_unless (gst_sparse_file_n_ranges (file) == 1);
|
||||||
|
expect_range_before (file, 110, 0, 250);
|
||||||
|
expect_range_after (file, 50, 0, 250);
|
||||||
|
expect_range_after (file, 100, 0, 250);
|
||||||
|
expect_range_before (file, 150, 0, 250);
|
||||||
|
fail_unless (gst_sparse_file_get_range_after (file, 250, &start,
|
||||||
|
&stop) == FALSE);
|
||||||
|
|
||||||
|
/* read work */
|
||||||
|
fail_unless (expect_read (file, 50, 150, 150, 50));
|
||||||
|
|
||||||
|
g_unlink (name);
|
||||||
|
gst_sparse_file_free (file);
|
||||||
|
g_free (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
static Suite *
|
||||||
|
gst_cachefile_suite (void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create ("cachefile");
|
||||||
|
TCase *tc_chain = tcase_create ("general");
|
||||||
|
|
||||||
|
suite_add_tcase (s, tc_chain);
|
||||||
|
tcase_add_test (tc_chain, test_write_read);
|
||||||
|
tcase_add_test (tc_chain, test_write_merge);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_CHECK_MAIN (gst_cachefile);
|
Loading…
Reference in a new issue