gstreamer/tests/check/elements/fake_http_src.c
Florin Apostol 78ce34b37f tests: fixed fake soup http src plugin
The soup http src changed the way it interprets the seek segment stop value.
Previously it was inclusive, now it is not (see commit
21c6da6764,
bug https://bugzilla.gnome.org/show_bug.cgi?id=748316)

Updated fake soup http src to also consider segment stop not inclusive.

https://bugzilla.gnome.org/show_bug.cgi?id=756322
2015-10-29 16:01:40 +00:00

433 lines
12 KiB
C

/* GStreamer unit test for MPEG-DASH
*
* Copyright (c) <2015> YouView TV Ltd
*
* 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 <gst/check/gstcheck.h>
#include <gst/base/gstbasesrc.h>
#include "fake_http_src.h"
#ifndef GST_PACKAGE_NAME
#define GST_PACKAGE_NAME "gst-plugins-bad"
#endif
#ifndef GST_PACKAGE_ORIGIN
#define GST_PACKAGE_ORIGIN "https://developer.gnome.org/gstreamer/"
#endif
#define GST_FAKE_SOUP_HTTP_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_FAKE_SOUP_HTTP_SRC, GstFakeSoupHTTPSrc))
#define GST_FAKE_SOUP_HTTP_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_FAKE_SOUP_HTTP_SRC, GstFakeSoupHTTPSrcClass))
#define GST_IS_FAKE_SOUP_HTTP_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FAKE_SOUP_HTTP_SRC))
#define GST_IS_FAKE_SOUP_HTTP_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_FAKE_SOUP_HTTP_SRC))
#define GST_FAKE_SOUP_HTTP_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_FAKE_SOUP_HTTP_SRC, GstFakeSoupHTTPSrcClass))
typedef struct _GstFakeSoupHTTPSrc
{
GstBaseSrc parent;
/* uri for which to retrieve data */
gchar *uri;
/* data to retrieve.
* If NULL, we will fake a buffer of size bytes, containing numbers in sequence
* 0, 4, 8, ...
* Each number is written on sizeof(int) bytes in little endian format
*/
const gchar *payload;
/* size of data to generate */
guint size;
/* position from where to retrieve data */
guint64 position;
/* index immediately after the last byte from the segment to be retrieved */
guint64 segment_end;
} GstFakeSoupHTTPSrc;
typedef struct _GstFakeSoupHTTPSrcClass
{
GstBaseSrcClass parent_class;
} GstFakeSoupHTTPSrcClass;
typedef struct _PluginInitContext
{
const gchar *name;
guint rank;
GType type;
} PluginInitContext;
static const GstFakeHttpSrcInputData *gst_fake_soup_http_src_inputData = NULL;
static GstStaticPadTemplate gst_dashdemux_test_source_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static void gst_fake_soup_http_src_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static void gst_fake_soup_http_src_finalize (GObject * object);
static gboolean gst_fake_soup_http_src_is_seekable (GstBaseSrc * basesrc);
static gboolean gst_fake_soup_http_src_do_seek (GstBaseSrc * basesrc,
GstSegment * segment);
static gboolean gst_fake_soup_http_src_start (GstBaseSrc * basesrc);
static gboolean gst_fake_soup_http_src_stop (GstBaseSrc * basesrc);
static gboolean gst_fake_soup_http_src_get_size (GstBaseSrc * basesrc,
guint64 * size);
static GstFlowReturn gst_fake_soup_http_src_create (GstBaseSrc * basesrc,
guint64 offset, guint length, GstBuffer ** ret);
#define _do_init \
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_fake_soup_http_src_uri_handler_init);
#define gst_fake_soup_http_src_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstFakeSoupHTTPSrc, gst_fake_soup_http_src,
GST_TYPE_BASE_SRC, _do_init);
static void
gst_fake_soup_http_src_class_init (GstFakeSoupHTTPSrcClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSrcClass *gstbasesrc_class;
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
gobject_class->finalize = gst_fake_soup_http_src_finalize;
gst_element_class_set_metadata (gstelement_class,
"Fake HTTP source element for unit tests",
"Source/Network",
"Use in unit tests", "Alex Ashley <alex.ashley@youview.com>");
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gst_dashdemux_test_source_template));
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_fake_soup_http_src_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_fake_soup_http_src_stop);
gstbasesrc_class->is_seekable =
GST_DEBUG_FUNCPTR (gst_fake_soup_http_src_is_seekable);
gstbasesrc_class->do_seek =
GST_DEBUG_FUNCPTR (gst_fake_soup_http_src_do_seek);
gstbasesrc_class->get_size =
GST_DEBUG_FUNCPTR (gst_fake_soup_http_src_get_size);
gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_fake_soup_http_src_create);
}
static void
gst_fake_soup_http_src_init (GstFakeSoupHTTPSrc * src)
{
src->uri = NULL;
src->payload = NULL;
src->position = 0;
src->size = 0;
src->segment_end = 0;
gst_base_src_set_blocksize (GST_BASE_SRC (src),
GST_FAKE_SOUP_HTTP_SRC_MAX_BUF_SIZE);
}
static void
gst_fake_soup_http_src_finalize (GObject * object)
{
GstFakeSoupHTTPSrc *src;
src = GST_FAKE_SOUP_HTTP_SRC (object);
g_free (src->uri);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gst_fake_soup_http_src_start (GstBaseSrc * basesrc)
{
GstFakeSoupHTTPSrc *src;
const GstFakeHttpSrcInputData *input = gst_fake_soup_http_src_inputData;
src = GST_FAKE_SOUP_HTTP_SRC (basesrc);
if (!src->uri) {
GST_WARNING ("gst_fake_soup_http_src_start without location");
return FALSE;
}
for (guint i = 0; input[i].uri; ++i) {
if (strcmp (input[i].uri, src->uri) == 0) {
src->payload = input[i].payload;
src->position = 0;
if (src->payload)
src->size = strlen (src->payload);
else
src->size = input[i].size;
src->segment_end = src->size;
gst_base_src_set_dynamic_size (basesrc, FALSE);
return TRUE;
}
}
GST_WARNING
("gst_fake_soup_http_src_start cannot find url '%s' in input data",
src->uri);
return FALSE;
}
static gboolean
gst_fake_soup_http_src_stop (GstBaseSrc * basesrc)
{
GstFakeSoupHTTPSrc *src;
src = GST_FAKE_SOUP_HTTP_SRC (basesrc);
src->payload = NULL;
src->position = 0;
src->size = 0;
return TRUE;
}
static gboolean
gst_fake_soup_http_src_is_seekable (GstBaseSrc * basesrc)
{
GstFakeSoupHTTPSrc *src;
src = GST_FAKE_SOUP_HTTP_SRC (basesrc);
/* if size is set, we can seek */
return src->size > 0;
}
static gboolean
gst_fake_soup_http_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment)
{
GstFakeSoupHTTPSrc *src;
GST_DEBUG ("gst_fake_soup_http_src_do_seek start = %" G_GUINT64_FORMAT,
segment->start);
src = GST_FAKE_SOUP_HTTP_SRC (basesrc);
/*
According to RFC7233, the range is inclusive:
The first-byte-pos value in a byte-range-spec gives the byte-offset
of the first byte in a range. The last-byte-pos value gives the
byte-offset of the last byte in the range; that is, the byte
positions specified are inclusive. Byte offsets start at zero.
*/
if (!src->uri) {
return FALSE;
}
if (segment->start >= src->size) {
return FALSE;
}
if (segment->stop != -1 && segment->stop > src->size) {
return FALSE;
}
src->position = segment->start;
if (segment->stop != -1) {
src->segment_end = segment->stop;
}
return TRUE;
}
static gboolean
gst_fake_soup_http_src_get_size (GstBaseSrc * basesrc, guint64 * size)
{
GstFakeSoupHTTPSrc *src;
const GstFakeHttpSrcInputData *input = gst_fake_soup_http_src_inputData;
src = GST_FAKE_SOUP_HTTP_SRC (basesrc);
if (!src->uri) {
return FALSE;
}
/* if it was started (payload or size configured), size is set */
if (src->payload || src->size > 0) {
*size = src->size;
return TRUE;
}
/* it wasn't started, compute the size */
for (guint i = 0; input[i].uri; ++i) {
if (strcmp (input[i].uri, src->uri) == 0) {
if (input[i].payload)
*size = strlen (input[i].payload);
else
*size = input[i].size;
return TRUE;
}
}
return FALSE;
}
static GstFlowReturn
gst_fake_soup_http_src_create (GstBaseSrc * basesrc, guint64 offset,
guint length, GstBuffer ** ret)
{
GstFakeSoupHTTPSrc *src;
guint bytes_read;
GstBuffer *buf;
src = GST_FAKE_SOUP_HTTP_SRC (basesrc);
GST_DEBUG ("gst_fake_soup_http_src_create feeding from %" G_GUINT64_FORMAT,
src->position);
if (src->uri == NULL) {
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
return GST_FLOW_ERROR;
}
bytes_read = MIN ((src->segment_end - src->position),
GST_FAKE_SOUP_HTTP_SRC_MAX_BUF_SIZE);
if (bytes_read == 0) {
return GST_FLOW_EOS;
}
buf = gst_buffer_new_allocate (NULL, bytes_read, NULL);
fail_if (buf == NULL, "Not enough memory to allocate buffer");
if (src->payload) {
gst_buffer_fill (buf, 0, src->payload + src->position, bytes_read);
} else {
GstMapInfo info;
guint pattern;
pattern = src->position - src->position % sizeof (pattern);
gst_buffer_map (buf, &info, GST_MAP_WRITE);
for (guint64 i = 0; i < bytes_read; ++i) {
gchar pattern_byte_to_write = (src->position + i) % sizeof (pattern);
if (pattern_byte_to_write == 0) {
pattern = src->position + i;
}
info.data[i] = (pattern >> (pattern_byte_to_write * 8)) & 0xFF;
}
gst_buffer_unmap (buf, &info);
}
GST_BUFFER_OFFSET (buf) = src->position;
GST_BUFFER_OFFSET_END (buf) = src->position + bytes_read;
*ret = buf;
src->position += bytes_read;
return GST_FLOW_OK;
}
static gboolean
gst_fake_soup_http_src_set_location (GstFakeSoupHTTPSrc * src,
const gchar * uri, GError ** error)
{
g_free (src->uri);
src->uri = g_strdup (uri);
return TRUE;
}
static GstURIType
gst_fake_soup_http_src_uri_get_type (GType type)
{
return GST_URI_SRC;
}
static const gchar *const *
gst_fake_soup_http_src_uri_get_protocols (GType type)
{
static const gchar *protocols[] = { "http", NULL };
return protocols;
}
static gchar *
gst_fake_soup_http_src_uri_get_uri (GstURIHandler * handler)
{
GstFakeSoupHTTPSrc *src = GST_FAKE_SOUP_HTTP_SRC (handler);
return g_strdup (src->uri);
}
static gboolean
gst_fake_soup_http_src_uri_set_uri (GstURIHandler * handler, const gchar * uri,
GError ** err)
{
GstFakeSoupHTTPSrc *src = GST_FAKE_SOUP_HTTP_SRC (handler);
return gst_fake_soup_http_src_set_location (src, uri, err);
}
static void
gst_fake_soup_http_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
iface->get_type = gst_fake_soup_http_src_uri_get_type;
iface->get_protocols = gst_fake_soup_http_src_uri_get_protocols;
iface->get_uri = gst_fake_soup_http_src_uri_get_uri;
iface->set_uri = gst_fake_soup_http_src_uri_set_uri;
}
static gboolean
gst_fake_soup_http_src_plugin_init_func (GstPlugin * plugin, gpointer user_data)
{
PluginInitContext *context = (PluginInitContext *) user_data;
gboolean ret;
ret =
gst_element_register (plugin, context->name, context->rank,
context->type);
return ret;
}
gboolean
gst_fake_soup_http_src_register_plugin (GstRegistry * registry,
const gchar * name)
{
gboolean ret;
PluginInitContext context;
context.name = name;
context.rank = GST_RANK_PRIMARY + 1;
context.type = GST_TYPE_FAKE_SOUP_HTTP_SRC;
ret = gst_plugin_register_static_full (GST_VERSION_MAJOR, /* version */
GST_VERSION_MINOR, /* version */
name, /* name */
"Replaces a souphttpsrc plugin and returns predefined data.", /* description */
gst_fake_soup_http_src_plugin_init_func, /* init function */
"0.0.0", /* version string */
GST_LICENSE_UNKNOWN, /* license */
__FILE__, /* source */
GST_PACKAGE_NAME, /* package */
GST_PACKAGE_ORIGIN, /* origin */
&context /* user_data */
);
return ret;
}
/**
* gst_fake_soup_http_src_set_input_data:
* @input: array of #GstFakeHttpSrcInputData that is used when
* responding to a request. The last entry in the array must
* have the uri field set to NULL
*/
void
gst_fake_soup_http_src_set_input_data (const GstFakeHttpSrcInputData * input)
{
gst_fake_soup_http_src_inputData = input;
}