2015-11-10 13:13:35 +00:00
|
|
|
/* HTTP source element for use in tests
|
|
|
|
*
|
|
|
|
* 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 "test_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_TEST_HTTP_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TEST_HTTP_SRC, GstTestHTTPSrc))
|
|
|
|
#define GST_TEST_HTTP_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TEST_HTTP_SRC, GstTestHTTPSrcClass))
|
|
|
|
#define GST_IS_TEST_HTTP_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TEST_HTTP_SRC))
|
|
|
|
#define GST_IS_TEST_HTTP_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TEST_HTTP_SRC))
|
|
|
|
#define GST_TEST_HTTP_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TEST_HTTP_SRC, GstTestHTTPSrcClass))
|
|
|
|
|
|
|
|
#define DEFAULT_USER_AGENT "GStreamer testhttpsrc "
|
|
|
|
#define DEFAULT_COMPRESS FALSE
|
|
|
|
#define DEFAULT_HTTP_METHOD NULL
|
|
|
|
#define DEFAULT_KEEP_ALIVE FALSE
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
PROP_USER_AGENT,
|
|
|
|
PROP_EXTRA_HEADERS,
|
|
|
|
PROP_COMPRESS,
|
|
|
|
PROP_KEEP_ALIVE,
|
|
|
|
PROP_METHOD,
|
|
|
|
PROP_LAST
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
METHOD_INVALID,
|
|
|
|
METHOD_GET,
|
|
|
|
METHOD_POST,
|
|
|
|
METHOD_HEAD,
|
|
|
|
METHOD_OPTIONS
|
|
|
|
} HttpMethod;
|
|
|
|
|
|
|
|
typedef struct _GstTestHTTPSrcMethodName
|
|
|
|
{
|
|
|
|
const gchar *name;
|
|
|
|
HttpMethod method;
|
|
|
|
} GstTestHTTPSrcMethodName;
|
|
|
|
|
|
|
|
static const GstTestHTTPSrcMethodName gst_test_http_src_methods[] = {
|
|
|
|
{"GET", METHOD_GET},
|
|
|
|
{"POST", METHOD_POST},
|
|
|
|
{"HEAD", METHOD_HEAD},
|
|
|
|
{"OPTIONS", METHOD_OPTIONS},
|
|
|
|
{NULL, METHOD_INVALID}
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _GstTestHTTPSrc
|
|
|
|
{
|
|
|
|
GstBaseSrc parent;
|
|
|
|
|
|
|
|
GMutex mutex;
|
|
|
|
|
|
|
|
GstTestHTTPSrcInput input;
|
|
|
|
|
|
|
|
gchar *uri; /* the uri for which data is being requested */
|
|
|
|
gboolean compress;
|
|
|
|
gboolean keep_alive;
|
|
|
|
gchar *http_method_name;
|
|
|
|
HttpMethod http_method;
|
|
|
|
GstStructure *extra_headers;
|
|
|
|
gchar *user_agent;
|
|
|
|
|
|
|
|
guint64 position;
|
|
|
|
/* index immediately after the last byte from the segment to be retrieved */
|
|
|
|
guint64 segment_end;
|
|
|
|
|
|
|
|
GstEvent *http_headers_event;
|
|
|
|
gboolean duration_changed;
|
|
|
|
} GstTestHTTPSrc;
|
|
|
|
|
|
|
|
typedef struct _GstTestHTTPSrcClass
|
|
|
|
{
|
|
|
|
GstBaseSrcClass parent_class;
|
|
|
|
} GstTestHTTPSrcClass;
|
|
|
|
|
|
|
|
typedef struct _PluginInitContext
|
|
|
|
{
|
|
|
|
const gchar *name;
|
|
|
|
guint rank;
|
|
|
|
GType type;
|
|
|
|
} PluginInitContext;
|
|
|
|
|
|
|
|
static const GstTestHTTPSrcCallbacks *gst_test_http_src_callbacks = NULL;
|
|
|
|
static gpointer gst_test_http_src_callback_user_data = NULL;
|
|
|
|
static guint gst_test_http_src_blocksize = 0;
|
|
|
|
|
|
|
|
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_test_http_src_uri_handler_init (gpointer g_iface,
|
|
|
|
gpointer iface_data);
|
|
|
|
static void gst_test_http_src_finalize (GObject * object);
|
|
|
|
static gboolean gst_test_http_src_is_seekable (GstBaseSrc * basesrc);
|
|
|
|
static gboolean gst_test_http_src_do_seek (GstBaseSrc * basesrc,
|
|
|
|
GstSegment * segment);
|
|
|
|
static gboolean gst_test_http_src_start (GstBaseSrc * basesrc);
|
|
|
|
static gboolean gst_test_http_src_stop (GstBaseSrc * basesrc);
|
|
|
|
static gboolean gst_test_http_src_get_size (GstBaseSrc * basesrc,
|
|
|
|
guint64 * size);
|
|
|
|
static GstFlowReturn gst_test_http_src_create (GstBaseSrc * basesrc,
|
|
|
|
guint64 offset, guint length, GstBuffer ** ret);
|
|
|
|
static void gst_test_http_src_set_property (GObject * object, guint prop_id,
|
|
|
|
const GValue * value, GParamSpec * pspec);
|
|
|
|
static void gst_test_http_src_get_property (GObject * object, guint prop_id,
|
|
|
|
GValue * value, GParamSpec * pspec);
|
|
|
|
|
|
|
|
|
|
|
|
#define _do_init \
|
|
|
|
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_test_http_src_uri_handler_init);
|
|
|
|
|
|
|
|
#define gst_test_http_src_parent_class parent_class
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GstTestHTTPSrc, gst_test_http_src,
|
|
|
|
GST_TYPE_BASE_SRC, _do_init);
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_test_http_src_class_init (GstTestHTTPSrcClass * 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->set_property = gst_test_http_src_set_property;
|
|
|
|
gobject_class->get_property = gst_test_http_src_get_property;
|
|
|
|
gobject_class->finalize = gst_test_http_src_finalize;
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_COMPRESS,
|
|
|
|
g_param_spec_boolean ("compress", "Compress",
|
|
|
|
"Allow compressed content encodings",
|
|
|
|
DEFAULT_COMPRESS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_EXTRA_HEADERS,
|
|
|
|
g_param_spec_boxed ("extra-headers", "Extra Headers",
|
|
|
|
"Extra headers to append to the HTTP request",
|
|
|
|
GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_KEEP_ALIVE,
|
|
|
|
g_param_spec_boolean ("keep-alive", "keep-alive",
|
|
|
|
"Use HTTP persistent connections", DEFAULT_KEEP_ALIVE,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_METHOD,
|
|
|
|
g_param_spec_string ("method", "HTTP method",
|
|
|
|
"The HTTP method to use (GET, HEAD, OPTIONS, etc)",
|
|
|
|
DEFAULT_HTTP_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_USER_AGENT,
|
|
|
|
g_param_spec_string ("user-agent", "User-Agent",
|
|
|
|
"Value of the User-Agent HTTP request header field",
|
|
|
|
DEFAULT_USER_AGENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
|
|
gst_element_class_set_metadata (gstelement_class,
|
|
|
|
"Test HTTP source element for unit tests",
|
|
|
|
"Source/Network",
|
|
|
|
"Use in unit tests", "Alex Ashley <alex.ashley@youview.com>");
|
2016-03-04 06:50:26 +00:00
|
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
|
|
&gst_dashdemux_test_source_template);
|
2015-11-10 13:13:35 +00:00
|
|
|
|
|
|
|
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_test_http_src_start);
|
|
|
|
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_test_http_src_stop);
|
|
|
|
gstbasesrc_class->is_seekable =
|
|
|
|
GST_DEBUG_FUNCPTR (gst_test_http_src_is_seekable);
|
|
|
|
gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_test_http_src_do_seek);
|
|
|
|
gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_test_http_src_get_size);
|
|
|
|
gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_test_http_src_create);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_test_http_src_init (GstTestHTTPSrc * src)
|
|
|
|
{
|
|
|
|
g_mutex_init (&src->mutex);
|
|
|
|
src->uri = NULL;
|
|
|
|
memset (&src->input, 0, sizeof (src->input));
|
|
|
|
src->compress = FALSE;
|
|
|
|
src->keep_alive = FALSE;
|
|
|
|
src->http_method_name = NULL;
|
|
|
|
src->http_method = METHOD_GET;
|
|
|
|
src->user_agent = NULL;
|
|
|
|
src->position = 0;
|
|
|
|
src->segment_end = 0;
|
|
|
|
src->http_headers_event = NULL;
|
|
|
|
src->duration_changed = FALSE;
|
|
|
|
if (gst_test_http_src_blocksize)
|
|
|
|
gst_base_src_set_blocksize (GST_BASE_SRC (src),
|
|
|
|
gst_test_http_src_blocksize);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_test_http_src_reset_input (GstTestHTTPSrc * src)
|
|
|
|
{
|
|
|
|
src->input.context = NULL;
|
|
|
|
src->input.size = 0;
|
|
|
|
src->input.status_code = 0;
|
|
|
|
if (src->input.request_headers) {
|
|
|
|
gst_structure_free (src->input.request_headers);
|
|
|
|
src->input.request_headers = NULL;
|
|
|
|
}
|
|
|
|
if (src->input.response_headers) {
|
|
|
|
gst_structure_free (src->input.response_headers);
|
|
|
|
src->input.response_headers = NULL;
|
|
|
|
}
|
|
|
|
if (src->http_headers_event) {
|
|
|
|
gst_event_unref (src->http_headers_event);
|
|
|
|
src->http_headers_event = NULL;
|
|
|
|
}
|
|
|
|
if (src->extra_headers) {
|
|
|
|
gst_structure_free (src->extra_headers);
|
|
|
|
src->extra_headers = NULL;
|
|
|
|
}
|
2016-01-14 18:27:50 +00:00
|
|
|
g_free (src->http_method_name);
|
|
|
|
src->http_method_name = NULL;
|
|
|
|
g_free (src->user_agent);
|
|
|
|
src->user_agent = NULL;
|
2015-11-10 13:13:35 +00:00
|
|
|
src->duration_changed = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_test_http_src_finalize (GObject * object)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src;
|
|
|
|
|
|
|
|
src = GST_TEST_HTTP_SRC (object);
|
|
|
|
|
|
|
|
g_free (src->uri);
|
|
|
|
gst_test_http_src_reset_input (src);
|
|
|
|
g_mutex_clear (&src->mutex);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_test_http_src_start (GstBaseSrc * basesrc)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src;
|
|
|
|
GstStructure *http_headers;
|
|
|
|
|
|
|
|
src = GST_TEST_HTTP_SRC (basesrc);
|
|
|
|
g_mutex_lock (&src->mutex);
|
|
|
|
gst_test_http_src_reset_input (src);
|
|
|
|
if (!src->uri) {
|
|
|
|
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (("No URL set.")),
|
|
|
|
("Missing location property"));
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (!gst_test_http_src_callbacks) {
|
|
|
|
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
|
|
|
|
(("Callbacks not registered.")), ("Callbacks not registered"));
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (!gst_test_http_src_callbacks->src_start) {
|
|
|
|
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
|
|
|
|
(("src_start callback not defined.")),
|
|
|
|
("src_start callback not registered"));
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (!gst_test_http_src_callbacks->src_start (src, src->uri, &src->input,
|
|
|
|
gst_test_http_src_callback_user_data)) {
|
|
|
|
if (src->input.status_code == 0) {
|
|
|
|
src->input.status_code = 404;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (src->input.status_code == 0) {
|
|
|
|
src->input.status_code = 200;
|
|
|
|
}
|
|
|
|
src->position = 0;
|
|
|
|
src->segment_end = src->input.size;
|
|
|
|
gst_base_src_set_dynamic_size (basesrc, FALSE);
|
|
|
|
basesrc->segment.duration = src->input.size;
|
|
|
|
src->duration_changed = TRUE;
|
|
|
|
}
|
|
|
|
http_headers = gst_structure_new_empty ("http-headers");
|
|
|
|
gst_structure_set (http_headers, "uri", G_TYPE_STRING, src->uri, NULL);
|
|
|
|
if (!src->input.request_headers) {
|
2016-02-16 14:26:55 +00:00
|
|
|
src->input.request_headers =
|
|
|
|
gst_structure_new_empty (TEST_HTTP_SRC_REQUEST_HEADERS_NAME);
|
2015-11-10 13:13:35 +00:00
|
|
|
}
|
|
|
|
if (!gst_structure_has_field_typed (src->input.request_headers,
|
|
|
|
"User-Agent", G_TYPE_STRING)) {
|
|
|
|
gst_structure_set (src->input.request_headers,
|
|
|
|
"User-Agent", G_TYPE_STRING,
|
|
|
|
src->user_agent ? src->user_agent : DEFAULT_USER_AGENT, NULL);
|
|
|
|
}
|
|
|
|
if (!gst_structure_has_field_typed (src->input.request_headers,
|
|
|
|
"Connection", G_TYPE_STRING)) {
|
|
|
|
gst_structure_set (src->input.request_headers,
|
|
|
|
"Connection", G_TYPE_STRING,
|
|
|
|
src->keep_alive ? "Keep-Alive" : "Close", NULL);
|
|
|
|
}
|
|
|
|
if (src->compress
|
|
|
|
&& !gst_structure_has_field_typed (src->input.request_headers,
|
|
|
|
"Accept-Encoding", G_TYPE_STRING)) {
|
|
|
|
gst_structure_set (src->input.request_headers, "Accept-Encoding",
|
|
|
|
G_TYPE_STRING, "compress, gzip", NULL);
|
|
|
|
}
|
2016-02-16 14:26:55 +00:00
|
|
|
gst_structure_set (http_headers, TEST_HTTP_SRC_REQUEST_HEADERS_NAME,
|
|
|
|
GST_TYPE_STRUCTURE, src->input.request_headers, NULL);
|
2015-11-10 13:13:35 +00:00
|
|
|
if (!src->input.response_headers) {
|
2016-02-16 14:26:55 +00:00
|
|
|
src->input.response_headers =
|
|
|
|
gst_structure_new_empty (TEST_HTTP_SRC_RESPONSE_HEADERS_NAME);
|
2015-11-10 13:13:35 +00:00
|
|
|
}
|
|
|
|
if (!gst_structure_has_field_typed (src->input.response_headers,
|
|
|
|
"Connection", G_TYPE_STRING)) {
|
|
|
|
gst_structure_set (src->input.response_headers,
|
|
|
|
"Connection", G_TYPE_STRING,
|
|
|
|
src->keep_alive ? "keep-alive" : "close", NULL);
|
|
|
|
}
|
|
|
|
if (!gst_structure_has_field_typed (src->input.response_headers,
|
|
|
|
"Date", G_TYPE_STRING)) {
|
|
|
|
GDateTime *now;
|
|
|
|
gchar *date_str;
|
|
|
|
|
|
|
|
now = g_date_time_new_now_local ();
|
|
|
|
fail_unless (now != NULL);
|
|
|
|
date_str = g_date_time_format (now, "%a, %e %b %Y %T %Z");
|
|
|
|
fail_unless (date_str != NULL);
|
|
|
|
gst_structure_set (src->input.response_headers,
|
|
|
|
"Date", G_TYPE_STRING, date_str, NULL);
|
|
|
|
g_free (date_str);
|
|
|
|
g_date_time_unref (now);
|
|
|
|
}
|
2016-02-16 14:26:55 +00:00
|
|
|
gst_structure_set (http_headers, TEST_HTTP_SRC_RESPONSE_HEADERS_NAME,
|
|
|
|
GST_TYPE_STRUCTURE, src->input.response_headers, NULL);
|
2015-11-10 13:13:35 +00:00
|
|
|
if (src->http_headers_event) {
|
|
|
|
gst_event_unref (src->http_headers_event);
|
|
|
|
}
|
|
|
|
src->http_headers_event =
|
|
|
|
gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, http_headers);
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_test_http_src_stop (GstBaseSrc * basesrc)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src;
|
|
|
|
|
|
|
|
src = GST_TEST_HTTP_SRC (basesrc);
|
|
|
|
g_mutex_lock (&src->mutex);
|
|
|
|
src->position = 0;
|
|
|
|
gst_test_http_src_reset_input (src);
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_test_http_src_is_seekable (GstBaseSrc * basesrc)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src;
|
|
|
|
gboolean ret;
|
|
|
|
|
|
|
|
src = GST_TEST_HTTP_SRC (basesrc);
|
|
|
|
g_mutex_lock (&src->mutex);
|
|
|
|
/* if size is set, we can seek */
|
|
|
|
ret = src->input.size > 0;
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_test_http_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src = GST_TEST_HTTP_SRC (basesrc);
|
|
|
|
|
|
|
|
GST_DEBUG ("gst_test_http_src_do_seek start = %" G_GUINT64_FORMAT,
|
|
|
|
segment->start);
|
|
|
|
|
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
g_mutex_lock (&src->mutex);
|
|
|
|
if (!src->uri) {
|
|
|
|
GST_DEBUG ("attempt to seek before URI set");
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (src->input.status_code >= 200 && src->input.status_code < 300) {
|
|
|
|
if (segment->start >= src->input.size) {
|
|
|
|
GST_DEBUG ("attempt to seek to %" G_GUINT64_FORMAT " but size is %"
|
|
|
|
G_GUINT64_FORMAT, segment->start, src->input.size);
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (segment->stop != -1 && segment->stop > src->input.size) {
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
GST_DEBUG ("Attempt to seek on a URL that will generate HTTP error %u",
|
|
|
|
src->input.status_code);
|
|
|
|
}
|
|
|
|
src->position = segment->start;
|
|
|
|
|
|
|
|
if (segment->stop != -1) {
|
|
|
|
src->segment_end = segment->stop;
|
|
|
|
} else {
|
|
|
|
src->segment_end = src->input.size;
|
|
|
|
}
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_test_http_src_get_size (GstBaseSrc * basesrc, guint64 * size)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src;
|
|
|
|
|
|
|
|
src = GST_TEST_HTTP_SRC (basesrc);
|
|
|
|
|
|
|
|
g_mutex_lock (&src->mutex);
|
|
|
|
/* if it was started, size is set */
|
|
|
|
if (src->uri && src->input.status_code >= 200 && src->input.status_code < 300) {
|
|
|
|
*size = src->input.size;
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/* cannot get the size if it wasn't started */
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GstFlowReturn
|
|
|
|
gst_test_http_src_create (GstBaseSrc * basesrc, guint64 offset,
|
|
|
|
guint length, GstBuffer ** retbuf)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src = GST_TEST_HTTP_SRC (basesrc);
|
|
|
|
guint bytes_read;
|
|
|
|
GstFlowReturn ret = GST_FLOW_OK;
|
|
|
|
guint blocksize;
|
|
|
|
|
|
|
|
fail_unless (gst_test_http_src_callbacks != NULL);
|
|
|
|
fail_unless (gst_test_http_src_callbacks->src_create != NULL);
|
|
|
|
|
|
|
|
GST_OBJECT_LOCK (src);
|
|
|
|
blocksize = basesrc->blocksize;
|
|
|
|
GST_OBJECT_UNLOCK (src);
|
|
|
|
|
|
|
|
g_mutex_lock (&src->mutex);
|
|
|
|
GST_DEBUG ("gst_test_http_src_create feeding from %" G_GUINT64_FORMAT,
|
|
|
|
src->position);
|
|
|
|
if (src->uri == NULL) {
|
|
|
|
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return GST_FLOW_ERROR;
|
|
|
|
}
|
|
|
|
if (src->input.status_code < 200 || src->input.status_code >= 300) {
|
|
|
|
GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("%s",
|
|
|
|
"Generated requested error"), ("%s (%d), URL: %s, Redirect to: %s",
|
|
|
|
"Generated requested error", src->input.status_code, src->uri,
|
|
|
|
GST_STR_NULL (NULL)));
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return GST_FLOW_ERROR;
|
|
|
|
}
|
|
|
|
if (src->http_method == METHOD_INVALID) {
|
|
|
|
GST_ELEMENT_ERROR (src, RESOURCE, READ, ("%s",
|
|
|
|
"Invalid HTTP method"), ("%s (%s), URL: %s",
|
|
|
|
"Invalid HTTP method", src->http_method_name, src->uri));
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return GST_FLOW_ERROR;
|
|
|
|
} else if (src->http_method == METHOD_HEAD) {
|
|
|
|
ret = GST_FLOW_EOS;
|
|
|
|
goto http_events;
|
|
|
|
}
|
|
|
|
fail_unless_equals_uint64 (offset, src->position);
|
|
|
|
bytes_read = MIN ((src->segment_end - src->position), blocksize);
|
|
|
|
if (bytes_read == 0) {
|
|
|
|
ret = GST_FLOW_EOS;
|
|
|
|
goto http_events;
|
|
|
|
}
|
|
|
|
ret = gst_test_http_src_callbacks->src_create (src,
|
|
|
|
offset, bytes_read, retbuf,
|
|
|
|
src->input.context, gst_test_http_src_callback_user_data);
|
|
|
|
if (ret != GST_FLOW_OK) {
|
|
|
|
goto http_events;
|
|
|
|
}
|
|
|
|
|
|
|
|
GST_BUFFER_OFFSET (*retbuf) = src->position;
|
|
|
|
GST_BUFFER_OFFSET_END (*retbuf) = src->position + bytes_read;
|
|
|
|
|
|
|
|
src->position += bytes_read;
|
|
|
|
http_events:
|
|
|
|
if (src->http_headers_event) {
|
|
|
|
gst_pad_push_event (GST_BASE_SRC_PAD (src), src->http_headers_event);
|
|
|
|
src->http_headers_event = NULL;
|
|
|
|
}
|
|
|
|
if (src->duration_changed) {
|
|
|
|
src->duration_changed = FALSE;
|
|
|
|
gst_element_post_message (GST_ELEMENT (src),
|
|
|
|
gst_message_new_duration_changed (GST_OBJECT (src)));
|
|
|
|
}
|
|
|
|
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_test_http_src_set_property (GObject * object, guint prop_id,
|
|
|
|
const GValue * value, GParamSpec * pspec)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src = GST_TEST_HTTP_SRC (object);
|
|
|
|
|
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_USER_AGENT:
|
|
|
|
g_free (src->user_agent);
|
|
|
|
src->user_agent = g_value_dup_string (value);
|
|
|
|
break;
|
|
|
|
case PROP_EXTRA_HEADERS:{
|
|
|
|
const GstStructure *s = gst_value_get_structure (value);
|
|
|
|
if (src->extra_headers)
|
|
|
|
gst_structure_free (src->extra_headers);
|
|
|
|
src->extra_headers = s ? gst_structure_copy (s) : NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PROP_COMPRESS:
|
|
|
|
src->compress = g_value_get_boolean (value);
|
|
|
|
GST_DEBUG ("Set compress=%s", src->compress ? "TRUE" : "FALSE");
|
|
|
|
break;
|
|
|
|
case PROP_KEEP_ALIVE:
|
|
|
|
src->keep_alive = g_value_get_boolean (value);
|
|
|
|
break;
|
2016-12-13 20:39:01 +00:00
|
|
|
case PROP_METHOD:{
|
|
|
|
guint i;
|
|
|
|
|
2015-11-10 13:13:35 +00:00
|
|
|
g_free (src->http_method_name);
|
|
|
|
src->http_method_name = g_value_dup_string (value);
|
|
|
|
src->http_method = METHOD_INVALID;
|
2016-12-13 20:39:01 +00:00
|
|
|
for (i = 0; gst_test_http_src_methods[i].name; ++i) {
|
2015-11-10 13:13:35 +00:00
|
|
|
if (strcmp (gst_test_http_src_methods[i].name,
|
|
|
|
src->http_method_name) == 0) {
|
|
|
|
src->http_method = gst_test_http_src_methods[i].method;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* we don't cause an error for an invalid method at this point,
|
|
|
|
as GstSoupHTTPSrc does not use the http_method_name string until
|
|
|
|
trying to open a connection.
|
|
|
|
*/
|
|
|
|
break;
|
2016-12-13 20:39:01 +00:00
|
|
|
}
|
2015-11-10 13:13:35 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_test_http_src_get_property (GObject * object, guint prop_id,
|
|
|
|
GValue * value, GParamSpec * pspec)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src = GST_TEST_HTTP_SRC (object);
|
|
|
|
|
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_USER_AGENT:
|
|
|
|
g_value_set_string (value, src->user_agent);
|
|
|
|
break;
|
|
|
|
case PROP_EXTRA_HEADERS:
|
|
|
|
gst_value_set_structure (value, src->extra_headers);
|
|
|
|
break;
|
|
|
|
case PROP_COMPRESS:
|
|
|
|
g_value_set_boolean (value, src->compress);
|
|
|
|
break;
|
|
|
|
case PROP_KEEP_ALIVE:
|
|
|
|
g_value_set_boolean (value, src->keep_alive);
|
|
|
|
break;
|
|
|
|
case PROP_METHOD:
|
|
|
|
g_value_set_string (value, src->http_method_name);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_test_http_src_set_location (GstTestHTTPSrc * src,
|
|
|
|
const gchar * uri, GError ** error)
|
|
|
|
{
|
|
|
|
g_mutex_lock (&src->mutex);
|
|
|
|
g_free (src->uri);
|
|
|
|
src->uri = g_strdup (uri);
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GstURIType
|
|
|
|
gst_test_http_src_uri_get_type (GType type)
|
|
|
|
{
|
|
|
|
return GST_URI_SRC;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const gchar *const *
|
|
|
|
gst_test_http_src_uri_get_protocols (GType type)
|
|
|
|
{
|
|
|
|
static const gchar *protocols[] = { "http", NULL };
|
|
|
|
|
|
|
|
return protocols;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gchar *
|
|
|
|
gst_test_http_src_uri_get_uri (GstURIHandler * handler)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src = GST_TEST_HTTP_SRC (handler);
|
|
|
|
gchar *ret;
|
|
|
|
g_mutex_lock (&src->mutex);
|
|
|
|
ret = g_strdup (src->uri);
|
|
|
|
g_mutex_unlock (&src->mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_test_http_src_uri_set_uri (GstURIHandler * handler, const gchar * uri,
|
|
|
|
GError ** err)
|
|
|
|
{
|
|
|
|
GstTestHTTPSrc *src = GST_TEST_HTTP_SRC (handler);
|
|
|
|
|
|
|
|
return gst_test_http_src_set_location (src, uri, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_test_http_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
|
|
|
|
{
|
|
|
|
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
|
|
|
|
|
|
|
|
iface->get_type = gst_test_http_src_uri_get_type;
|
|
|
|
iface->get_protocols = gst_test_http_src_uri_get_protocols;
|
|
|
|
iface->get_uri = gst_test_http_src_uri_get_uri;
|
|
|
|
iface->set_uri = gst_test_http_src_uri_set_uri;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_test_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_test_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_TEST_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_test_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gst_test_http_src_install_callbacks (const GstTestHTTPSrcCallbacks *
|
|
|
|
callbacks, gpointer user_data)
|
|
|
|
{
|
|
|
|
gst_test_http_src_callbacks = callbacks;
|
|
|
|
gst_test_http_src_callback_user_data = user_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gst_test_http_src_set_default_blocksize (guint blocksize)
|
|
|
|
{
|
|
|
|
gst_test_http_src_blocksize = blocksize;
|
|
|
|
}
|