#include <gst/sdp/gstmikey.h>
#include <gst/rtp/rtp.h>
-#include "gst/gst-i18n-plugin.h"
+#include <glib/gi18n-lib.h>
#include "gstrtspelements.h"
#include "gstrtspsrc.h"
SIGNAL_GET_PARAMETER,
SIGNAL_GET_PARAMETERS,
SIGNAL_SET_PARAMETER,
+ SIGNAL_PUSH_BACKCHANNEL_SAMPLE,
LAST_SIGNAL
};
#define DEFAULT_USER_AGENT "GStreamer/" PACKAGE_VERSION
#define DEFAULT_MAX_RTCP_RTP_TIME_DIFF 1000
#define DEFAULT_RFC7273_SYNC FALSE
+#define DEFAULT_ADD_REFERENCE_TIMESTAMP_META FALSE
#define DEFAULT_MAX_TS_OFFSET_ADJUSTMENT G_GUINT64_CONSTANT(0)
#define DEFAULT_MAX_TS_OFFSET G_GINT64_CONSTANT(3000000000)
#define DEFAULT_VERSION GST_RTSP_VERSION_1_0
PROP_USER_AGENT,
PROP_MAX_RTCP_RTP_TIME_DIFF,
PROP_RFC7273_SYNC,
+ PROP_ADD_REFERENCE_TIMESTAMP_META,
PROP_MAX_TS_OFFSET_ADJUSTMENT,
PROP_MAX_TS_OFFSET,
PROP_DEFAULT_VERSION,
static GstFlowReturn gst_rtspsrc_push_backchannel_buffer (GstRTSPSrc * src,
guint id, GstSample * sample);
+static GstFlowReturn gst_rtspsrc_push_backchannel_sample (GstRTSPSrc * src,
+ guint id, GstSample * sample);
+
typedef struct
{
guint8 pt;
* TLS certificate validation flags used to validate server
* certificate.
*
+ * GLib guarantees that if certificate verification fails, at least one
+ * error will be set, but it does not guarantee that all possible errors
+ * will be set. Accordingly, you may not safely decide to ignore any
+ * particular type of error.
+ *
+ * For example, it would be incorrect to mask %G_TLS_CERTIFICATE_EXPIRED if
+ * you want to allow expired certificates, because this could potentially be
+ * the only error flag set even if other problems exist with the
+ * certificate.
+ *
* Since: 1.2.1
*/
g_object_class_install_property (gobject_class, PROP_TLS_VALIDATION_FLAGS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
+ * GstRTSPSrc:add-reference-timestamp-meta:
+ *
+ * When syncing to a RFC7273 clock, add #GstReferenceTimestampMeta
+ * to buffers with the original reconstructed reference clock timestamp.
+ *
+ * Since: 1.22
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_ADD_REFERENCE_TIMESTAMP_META,
+ g_param_spec_boolean ("add-reference-timestamp-meta",
+ "Add Reference Timestamp Meta",
+ "Add Reference Timestamp Meta to buffers with the original clock timestamp "
+ "before any adjustments when syncing to an RFC7273 clock.",
+ DEFAULT_ADD_REFERENCE_TIMESTAMP_META,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
* GstRTSPSrc:default-rtsp-version:
*
* The preferred RTSP version to use while negotiating the version with the server.
/**
* GstRTSPSrc::push-backchannel-buffer:
* @rtspsrc: a #GstRTSPSrc
+ * @id: stream ID where the sample should be sent
* @sample: RTP sample to send back
*
+ * Deprecated: 1.22: Use action signal GstRTSPSrc::push-backchannel-sample instead.
+ * IMPORTANT: Please note that this signal decrements the reference count
+ * of sample internally! So it cannot be used from other
+ * language bindings in general.
*
*/
gst_rtspsrc_signals[SIGNAL_PUSH_BACKCHANNEL_BUFFER] =
g_signal_new ("push-backchannel-buffer", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRTSPSrcClass,
- push_backchannel_buffer), NULL, NULL, NULL,
- GST_TYPE_FLOW_RETURN, 2, G_TYPE_UINT, GST_TYPE_SAMPLE);
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION | G_SIGNAL_DEPRECATED,
+ G_STRUCT_OFFSET (GstRTSPSrcClass, push_backchannel_buffer), NULL, NULL,
+ NULL, GST_TYPE_FLOW_RETURN, 2, G_TYPE_UINT, GST_TYPE_SAMPLE);
+
+ /**
+ * GstRTSPSrc::push-backchannel-sample:
+ * @rtspsrc: a #GstRTSPSrc
+ * @id: stream ID where the sample should be sent
+ * @sample: RTP sample to send back
+ *
+ * Since: 1.22
+ */
+ gst_rtspsrc_signals[SIGNAL_PUSH_BACKCHANNEL_SAMPLE] =
+ g_signal_new ("push-backchannel-sample", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION | G_SIGNAL_DEPRECATED,
+ G_STRUCT_OFFSET (GstRTSPSrcClass, push_backchannel_buffer), NULL, NULL,
+ NULL, GST_TYPE_FLOW_RETURN, 2, G_TYPE_UINT, GST_TYPE_SAMPLE);
/**
* GstRTSPSrc::get-parameter:
gstbin_class->handle_message = gst_rtspsrc_handle_message;
klass->push_backchannel_buffer = gst_rtspsrc_push_backchannel_buffer;
+ klass->push_backchannel_sample = gst_rtspsrc_push_backchannel_sample;
klass->get_parameter = GST_DEBUG_FUNCPTR (get_parameter);
klass->get_parameters = GST_DEBUG_FUNCPTR (get_parameters);
klass->set_parameter = GST_DEBUG_FUNCPTR (set_parameter);
src->user_agent = g_strdup (DEFAULT_USER_AGENT);
src->max_rtcp_rtp_time_diff = DEFAULT_MAX_RTCP_RTP_TIME_DIFF;
src->rfc7273_sync = DEFAULT_RFC7273_SYNC;
+ src->add_reference_timestamp_meta = DEFAULT_ADD_REFERENCE_TIMESTAMP_META;
src->max_ts_offset_adjustment = DEFAULT_MAX_TS_OFFSET_ADJUSTMENT;
src->max_ts_offset = DEFAULT_MAX_TS_OFFSET;
src->max_ts_offset_is_set = FALSE;
if (rtspsrc->tls_interaction)
g_object_unref (rtspsrc->tls_interaction);
+ if (rtspsrc->initial_seek)
+ gst_event_unref (rtspsrc->initial_seek);
+
/* free locks */
g_rec_mutex_clear (&rtspsrc->stream_rec_lock);
g_rec_mutex_clear (&rtspsrc->state_rec_lock);
case PROP_RFC7273_SYNC:
rtspsrc->rfc7273_sync = g_value_get_boolean (value);
break;
+ case PROP_ADD_REFERENCE_TIMESTAMP_META:
+ rtspsrc->add_reference_timestamp_meta = g_value_get_boolean (value);
+ break;
case PROP_MAX_TS_OFFSET_ADJUSTMENT:
rtspsrc->max_ts_offset_adjustment = g_value_get_uint64 (value);
break;
case PROP_RFC7273_SYNC:
g_value_set_boolean (value, rtspsrc->rfc7273_sync);
break;
+ case PROP_ADD_REFERENCE_TIMESTAMP_META:
+ g_value_set_boolean (value, rtspsrc->add_reference_timestamp_meta);
+ break;
case PROP_MAX_TS_OFFSET_ADJUSTMENT:
g_value_set_uint64 (value, rtspsrc->max_ts_offset_adjustment);
break;
if (g_str_has_prefix (control_path, "rtsp://"))
stream->conninfo.location = g_strdup (control_path);
else {
+ const gchar *base;
+
+ base = get_aggregate_control (src);
if (g_strcmp0 (control_path, "*") == 0)
- control_path = "";
- /* handle url with query */
- if (src->conninfo.url && src->conninfo.url->query) {
- stream->conninfo.location =
- gst_rtsp_url_get_request_uri_with_control (src->conninfo.url,
- control_path);
- } else {
- const gchar *base;
- gboolean has_slash;
- const gchar *slash;
- const gchar *actual_control_path = NULL;
-
- base = get_aggregate_control (src);
- has_slash = g_str_has_suffix (base, "/");
- /* manage existence or non-existence of / in control path */
- if (control_path && strlen (control_path) > 0) {
- gboolean control_has_slash = g_str_has_prefix (control_path, "/");
-
- actual_control_path = control_path;
- if (has_slash && control_has_slash) {
- if (strlen (control_path) == 1) {
- actual_control_path = NULL;
- } else {
- actual_control_path = control_path + 1;
- }
- } else {
- has_slash = has_slash || control_has_slash;
- }
- }
- slash = (!has_slash && (actual_control_path != NULL)) ? "/" : "";
- /* concatenate the two strings, insert / when not present */
- stream->conninfo.location =
- g_strdup_printf ("%s%s%s", base, slash, control_path);
- }
+ control_path = g_strdup (base);
+ else
+ stream->conninfo.location = gst_uri_join_strings (base, control_path);
}
}
GST_DEBUG_OBJECT (src, " setup: %s",
}
static void
-gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush, gboolean playing,
- guint32 seqnum)
+gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush, gboolean playing)
{
GstEvent *event;
gint cmd;
if (flush) {
event = gst_event_new_flush_start ();
- gst_event_set_seqnum (event, seqnum);
GST_DEBUG_OBJECT (src, "start flush");
cmd = CMD_WAIT;
state = GST_STATE_PAUSED;
} else {
event = gst_event_new_flush_stop (TRUE);
- gst_event_set_seqnum (event, seqnum);
GST_DEBUG_OBJECT (src, "stop flush; playing %d", playing);
cmd = CMD_LOOP;
if (playing)
* blocking in preroll). */
if (flush) {
GST_DEBUG_OBJECT (src, "starting flush");
- gst_rtspsrc_flush (src, TRUE, FALSE, gst_event_get_seqnum (event));
+ gst_rtspsrc_flush (src, TRUE, FALSE);
} else {
if (src->task) {
gst_task_pause (src->task);
if (flush) {
/* if we started flush, we stop now */
GST_DEBUG_OBJECT (src, "stopping flush");
- gst_rtspsrc_flush (src, FALSE, playing, gst_event_get_seqnum (event));
+ gst_rtspsrc_flush (src, FALSE, playing);
}
/* now we did the seek and can activate the new segment values */
gst_event_set_group_id (event, src->group_id);
}
-static gboolean
-gst_rtspsrc_handle_src_sink_event (GstPad * pad, GstObject * parent,
+static GstEvent *
+gst_rtspsrc_update_src_event (GstRTSPSrc * self, GstRTSPStream * stream,
GstEvent * event)
{
- GstRTSPStream *stream;
- GstRTSPSrc *self = GST_RTSPSRC (GST_OBJECT_PARENT (parent));
-
- stream = gst_pad_get_element_private (pad);
-
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_STREAM_START:{
GChecksum *cs;
event = gst_event_new_stream_start (stream_id);
gst_rtspsrc_stream_start_event_add_group_id (self, event);
g_free (stream_id);
+
+ gst_event_set_seqnum (event, self->seek_seqnum);
break;
}
- case GST_EVENT_SEGMENT:
- if (self->seek_seqnum != GST_SEQNUM_INVALID)
- GST_EVENT_SEQNUM (event) = self->seek_seqnum;
- break;
default:
+ event = gst_event_make_writable (event);
+ gst_event_set_seqnum (event, self->seek_seqnum);
break;
}
+ return event;
+}
+
+static gboolean
+gst_rtspsrc_handle_src_sink_event (GstPad * pad, GstObject * parent,
+ GstEvent * event)
+{
+ GstRTSPStream *stream;
+ GstRTSPSrc *self = GST_RTSPSRC (GST_OBJECT_PARENT (parent));
+
+ stream = gst_pad_get_element_private (pad);
+
+ event = gst_rtspsrc_update_src_event (self, stream, event);
+
return gst_pad_push_event (stream->srcpad, event);
}
gst_rtspsrc_push_backchannel_buffer (GstRTSPSrc * src, guint id,
GstSample * sample)
{
+ GstFlowReturn res;
+
+ res = gst_rtspsrc_push_backchannel_sample (src, id, sample);
+
+ gst_sample_unref (sample);
+
+ return res;
+}
+
+static GstFlowReturn
+gst_rtspsrc_push_backchannel_sample (GstRTSPSrc * src, guint id,
+ GstSample * sample)
+{
GstFlowReturn res = GST_FLOW_OK;
GstRTSPStream *stream;
}
out:
- gst_sample_unref (sample);
-
return res;
}
switch (GST_EVENT_TYPE (info->data)) {
case GST_EVENT_SEGMENT:
- if (!gst_event_is_writable (info->data))
- info->data = gst_event_make_writable (info->data);
-
*segment_seqnum = gst_event_get_seqnum (info->data);
+ break;
default:
break;
}
return GST_PAD_PROBE_OK;
}
+typedef struct
+{
+ GstRTSPSrc *src;
+ GstRTSPStream *stream;
+} CopyStickyEventsData;
+
static gboolean
copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
{
- GstPad *gpad = GST_PAD_CAST (user_data);
+ CopyStickyEventsData *data = user_data;
+ GstEvent *new_event;
- GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
- gst_pad_store_sticky_event (gpad, *event);
+ GST_DEBUG_OBJECT (data->stream->srcpad, "send sticky event %" GST_PTR_FORMAT,
+ *event);
+ new_event =
+ gst_rtspsrc_update_src_event (data->src, data->stream,
+ gst_event_ref (*event));
+ gst_pad_store_sticky_event (data->stream->srcpad, new_event);
+ gst_event_unref (new_event);
return TRUE;
}
GstRTSPStream *stream;
gboolean all_added;
GstPad *internal_src;
+ CopyStickyEventsData copy_sticky_events_data;
GST_DEBUG_OBJECT (src, "got new manager pad %" GST_PTR_FORMAT, pad);
GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (stream->srcpad)));
gst_pad_set_element_private (internal_src, stream);
gst_pad_set_event_function (internal_src, gst_rtspsrc_handle_src_sink_event);
- gst_object_unref (internal_src);
gst_pad_set_event_function (stream->srcpad, gst_rtspsrc_handle_src_event);
gst_pad_set_query_function (stream->srcpad, gst_rtspsrc_handle_src_query);
gst_pad_set_active (stream->srcpad, TRUE);
- gst_pad_sticky_events_foreach (pad, copy_sticky_events, stream->srcpad);
+
+ copy_sticky_events_data.src = src;
+ copy_sticky_events_data.stream = stream;
+ gst_pad_sticky_events_foreach (pad, copy_sticky_events,
+ ©_sticky_events_data);
+
+ gst_object_unref (internal_src);
/* don't add the srcpad if this is a sendonly stream */
if (stream->is_backchannel)
{
GST_DEBUG_OBJECT (src, "setting stream for session %u to EOS", stream->id);
- if (stream->eos)
- goto was_eos;
-
- stream->eos = TRUE;
gst_rtspsrc_stream_push_event (src, stream, gst_event_new_eos ());
- return;
-
- /* ERRORS */
-was_eos:
- {
- GST_DEBUG_OBJECT (src, "stream for session %u was already EOS", stream->id);
- return;
- }
}
static void
GST_WARNING_OBJECT (src, "source %08x, stream %08x in session %u timed out",
ssrc, stream->ssrc, stream->id);
- if (ssrc == stream->ssrc)
- gst_rtspsrc_do_stream_eos (src, stream);
+ if (ssrc == stream->ssrc) {
+ GList *walk;
+ gboolean all_eos = TRUE;
+
+ GST_DEBUG_OBJECT (src, "setting stream for session %u to EOS", stream->id);
+ stream->eos = TRUE;
+
+ /* Only EOS all streams at once if they're all EOS. Otherwise it is
+ * possible for timed out streams to reappear at a later time time: they
+ * might just be inactive currently.
+ */
+
+ for (walk = src->streams; walk; walk = g_list_next (walk)) {
+ GstRTSPStream *stream = (GstRTSPStream *) walk->data;
+
+ /* Skip streams that were not set up at all */
+ if (!stream->setup)
+ continue;
+
+ if (!stream->eos) {
+ all_eos = FALSE;
+ break;
+ }
+ }
+
+ if (all_eos) {
+ GST_DEBUG_OBJECT (src, "sending EOS on all streams");
+ for (walk = src->streams; walk; walk = g_list_next (walk)) {
+ GstRTSPStream *stream = (GstRTSPStream *) walk->data;
+ gst_rtspsrc_stream_push_event (src, stream, gst_event_new_eos ());
+ }
+ }
+ }
}
static void
{
GST_DEBUG_OBJECT (stream->parent, "source in session %u is active",
stream->id);
+
+ stream->eos = FALSE;
}
static void
g_object_set (src->manager, "rfc7273-sync", src->rfc7273_sync, NULL);
}
+ if (g_object_class_find_property (klass, "add-reference-timestamp-meta")) {
+ g_object_set (src->manager, "add-reference-timestamp-meta",
+ src->add_reference_timestamp_meta, NULL);
+ }
+
if (src->use_pipeline_clock) {
if (g_object_class_find_property (klass, "use-pipeline-clock")) {
g_object_set (src->manager, "use-pipeline-clock", TRUE, NULL);
}
}
+static GstElement *
+element_make_from_addr (const GstURIType type, const char *addr_s,
+ int port, const char *name, GError ** error)
+{
+ GInetAddress *addr;
+ GstElement *element = NULL;
+ char *uri = NULL;
+
+ addr = g_inet_address_new_from_string (addr_s);
+ if (addr == NULL) {
+ /* Address is a hostname, not an IP address */
+ uri = g_strdup_printf ("udp://%s:%i", addr_s, port);
+ } else {
+ switch (g_inet_address_get_family (addr)) {
+ case G_SOCKET_FAMILY_IPV6:
+ uri = g_strdup_printf ("udp://[%s]:%i", addr_s, port);
+ break;
+ case G_SOCKET_FAMILY_INVALID:
+ GST_ERROR ("Unknown family type for %s", addr_s);
+ goto out;
+ case G_SOCKET_FAMILY_UNIX:
+ GST_ERROR ("Unexpected family type UNIX for %s", addr_s);
+ goto out;
+ case G_SOCKET_FAMILY_IPV4:
+ uri = g_strdup_printf ("udp://%s:%i", addr_s, port);
+ break;
+ }
+ }
+
+ element = gst_element_make_from_uri (type, uri, name, error);
+out:
+ g_clear_object (&addr);
+ g_free (uri);
+ return element;
+}
+
/* For multicast create UDP sources and join the multicast group. */
static gboolean
gst_rtspsrc_stream_configure_mcast (GstRTSPSrc * src, GstRTSPStream * stream,
GstRTSPTransport * transport, GstPad ** outpad)
{
- gchar *uri;
const gchar *destination;
gint min, max;
/* creating UDP source for RTP */
if (min != -1) {
- uri = g_strdup_printf ("udp://%s:%d", destination, min);
stream->udpsrc[0] =
- gst_element_make_from_uri (GST_URI_SRC, uri, NULL, NULL);
- g_free (uri);
+ element_make_from_addr (GST_URI_SRC, destination, min, NULL, NULL);
if (stream->udpsrc[0] == NULL)
goto no_element;
if (max != -1) {
GstCaps *caps;
- uri = g_strdup_printf ("udp://%s:%d", destination, max);
stream->udpsrc[1] =
- gst_element_make_from_uri (GST_URI_SRC, uri, NULL, NULL);
- g_free (uri);
+ element_make_from_addr (GST_URI_SRC, destination, max, NULL, NULL);
if (stream->udpsrc[1] == NULL)
goto no_element;
gint rtp_port, rtcp_port;
gboolean do_rtp, do_rtcp;
const gchar *destination;
- gchar *uri, *name;
+ gchar *name;
guint ttl = 0;
GSocket *socket;
GST_DEBUG_OBJECT (src, "configure RTP UDP sink for %s:%d", destination,
rtp_port);
- uri = g_strdup_printf ("udp://%s:%d", destination, rtp_port);
- stream->udpsink[0] =
- gst_element_make_from_uri (GST_URI_SINK, uri, NULL, NULL);
- g_free (uri);
+ stream->udpsink[0] = element_make_from_addr (GST_URI_SINK, destination,
+ rtp_port, NULL, NULL);
if (stream->udpsink[0] == NULL)
goto no_sink_element;
GST_DEBUG_OBJECT (src, "configure RTCP UDP sink for %s:%d", destination,
rtcp_port);
- uri = g_strdup_printf ("udp://%s:%d", destination, rtcp_port);
- stream->udpsink[1] =
- gst_element_make_from_uri (GST_URI_SINK, uri, NULL, NULL);
- g_free (uri);
+ stream->udpsink[1] = element_make_from_addr (GST_URI_SINK, destination,
+ rtcp_port, NULL, NULL);
if (stream->udpsink[1] == NULL)
goto no_sink_element;
add_backchannel_fakesink (src, stream, outpad);
gst_object_unref (outpad);
} else if (outpad) {
+ GstPad *internal_src;
+
GST_DEBUG_OBJECT (src, "creating ghostpad for stream %p", stream);
gst_pad_use_fixed_caps (outpad);
gst_object_unref (template);
g_free (name);
+ /* We intercept and modify the stream start event */
+ internal_src =
+ GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (stream->srcpad)));
+ gst_pad_set_element_private (internal_src, stream);
+ gst_pad_set_event_function (internal_src,
+ gst_rtspsrc_handle_src_sink_event);
+ gst_object_unref (internal_src);
+
+ gst_pad_set_event_function (stream->srcpad, gst_rtspsrc_handle_src_event);
+ gst_pad_set_query_function (stream->srcpad, gst_rtspsrc_handle_src_query);
+
gst_object_unref (outpad);
}
/* mark pad as ok */
if (!stream->setup)
goto done;
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ stream->eos = TRUE;
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ stream->eos = FALSE;
+ break;
+ default:
+ break;
+ }
+
if (stream->udpsrc[0]) {
GstEvent *sent_event;
- if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
- sent_event = gst_event_new_eos ();
+ if (stream->segment_seqnum[0] != GST_SEQNUM_INVALID) {
+ sent_event = gst_event_copy (event);
gst_event_set_seqnum (sent_event, stream->segment_seqnum[0]);
} else {
sent_event = gst_event_ref (event);
res = gst_element_send_event (stream->udpsrc[0], sent_event);
} else if (stream->channelpad[0]) {
- gst_event_ref (event);
+ GstEvent *sent_event;
+
+ sent_event = gst_event_copy (event);
+ gst_event_set_seqnum (sent_event, src->seek_seqnum);
+
if (GST_PAD_IS_SRC (stream->channelpad[0]))
- res = gst_pad_push_event (stream->channelpad[0], event);
+ res = gst_pad_push_event (stream->channelpad[0], sent_event);
else
- res = gst_pad_send_event (stream->channelpad[0], event);
+ res = gst_pad_send_event (stream->channelpad[0], sent_event);
}
if (stream->udpsrc[1]) {
GstEvent *sent_event;
- if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
- sent_event = gst_event_new_eos ();
- if (stream->segment_seqnum[1] != GST_SEQNUM_INVALID) {
- gst_event_set_seqnum (sent_event, stream->segment_seqnum[1]);
- }
+ if (stream->segment_seqnum[1] != GST_SEQNUM_INVALID) {
+ sent_event = gst_event_copy (event);
+ gst_event_set_seqnum (sent_event, stream->segment_seqnum[1]);
} else {
sent_event = gst_event_ref (event);
}
res &= gst_element_send_event (stream->udpsrc[1], sent_event);
} else if (stream->channelpad[1]) {
- gst_event_ref (event);
+ GstEvent *sent_event;
+
+ sent_event = gst_event_copy (event);
+ gst_event_set_seqnum (sent_event, src->seek_seqnum);
+
if (GST_PAD_IS_SRC (stream->channelpad[1]))
- res &= gst_pad_push_event (stream->channelpad[1], event);
+ res &= gst_pad_push_event (stream->channelpad[1], sent_event);
else
- res &= gst_pad_send_event (stream->channelpad[1], event);
+ res &= gst_pad_send_event (stream->channelpad[1], sent_event);
}
done:
}
connect_error:
{
- gchar *str = gst_rtsp_strresult (res);
GstFlowReturn ret;
src->conninfo.connected = FALSE;
if (res != GST_RTSP_EINTR) {
+ gchar *str = gst_rtsp_strresult (res);
#ifdef TIZEN_FEATURE_RTSP_MODIFICATION
gst_rtspsrc_post_error_message (src, GST_RTSPSRC_ERROR_CONNECTION_FAIL,
"Could not connect to server.");
}
handle_request_failed:
{
- gchar *str = gst_rtsp_strresult (res);
GstFlowReturn ret;
gst_rtsp_message_unset (&message);
if (res != GST_RTSP_EINTR) {
+ gchar *str = gst_rtsp_strresult (res);
#ifdef TIZEN_FEATURE_RTSP_MODIFICATION
gst_rtspsrc_post_error_message (src,
GST_RTSPSRC_ERROR_SERVICE_UNAVAILABLE,
*
* At the moment, for Basic auth, we just do a minimal check and don't
* even parse out the realm */
-static void
+static gboolean
gst_rtspsrc_parse_auth_hdr (GstRTSPMessage * response,
GstRTSPAuthMethod * methods, GstRTSPConnection * conn, gboolean * stale)
{
GstRTSPAuthCredential **credentials, **credential;
- g_return_if_fail (response != NULL);
- g_return_if_fail (methods != NULL);
- g_return_if_fail (stale != NULL);
+ g_return_val_if_fail (response != NULL, FALSE);
+ g_return_val_if_fail (methods != NULL, FALSE);
+ g_return_val_if_fail (stale != NULL, FALSE);
credentials =
gst_rtsp_message_parse_auth_credentials (response,
GST_RTSP_HDR_WWW_AUTHENTICATE);
if (!credentials)
- return;
+ return FALSE;
credential = credentials;
while (*credential) {
}
gst_rtsp_auth_credentials_free (credentials);
+
+ return TRUE;
}
/**
GstRTSPConnection *conn;
gboolean stale = FALSE;
+ g_return_val_if_fail (response != NULL, FALSE);
+
conn = src->conninfo.connection;
- /* Identify the available auth methods and see if any are supported */
- gst_rtspsrc_parse_auth_hdr (response, &avail_methods, conn, &stale);
+ /* Identify the available auth methods and see if any are supported. If no
+ * headers were found, propagate the HTTP error. */
+ if (!gst_rtspsrc_parse_auth_hdr (response, &avail_methods, conn, &stale))
+ goto propagate_error;
if (avail_methods == GST_RTSP_AUTH_NONE)
goto no_auth_available;
* already, request a username and passwd from the application via some kind
* of credentials request message */
- /* If we don't have a username and passwd at this point, bail out. */
+ /* If we don't have a username and passwd at this point, bail out and
+ * propagate the normal NOT_AUTHORIZED error. */
if (user == NULL || pass == NULL)
- goto no_user_pass;
+ goto propagate_error;
/* Try to configure for each available authentication method, strongest to
* weakest */
#endif
return FALSE;
}
-no_user_pass:
+
+propagate_error:
{
/* We don't fire an error message, we just return FALSE and let the
- * normal NOT_AUTHORIZED error be propagated */
+ * normal error be propagated */
return FALSE;
}
}
GstRTSPConnInfo *conninfo;
gchar *transports;
gint retry = 0;
+ gboolean tried_non_compliant_url = FALSE;
guint mask = 0;
gboolean selected;
GstCaps *caps;
continue;
else
goto retry;
+ case GST_RTSP_STS_BAD_REQUEST:
+ case GST_RTSP_STS_NOT_FOUND:
+ /* There are various non-compliant servers that don't require control
+ * URLs that are not resolved correctly but instead are just appended.
+ * See e.g.
+ * https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/922
+ * https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1447
+ */
+ if (!tried_non_compliant_url && stream->control_url
+ && !g_str_has_prefix (stream->control_url, "rtsp://")) {
+ const gchar *base;
+
+ gst_rtsp_message_unset (&request);
+ gst_rtsp_message_unset (&response);
+ gst_rtspsrc_stream_free_udp (stream);
+
+ g_free (stream->conninfo.location);
+ base = get_aggregate_control (src);
+
+ /* Make sure to not accumulate too many `/` */
+ if ((g_str_has_suffix (base, "/")
+ && !g_str_has_suffix (stream->control_url, "/"))
+ || (!g_str_has_suffix (base, "/")
+ && g_str_has_suffix (stream->control_url, "/"))
+ )
+ stream->conninfo.location =
+ g_strconcat (base, stream->control_url, NULL);
+ else if (g_str_has_suffix (base, "/")
+ && g_str_has_suffix (stream->control_url, "/"))
+ stream->conninfo.location =
+ g_strconcat (base, stream->control_url + 1, NULL);
+ else
+ stream->conninfo.location =
+ g_strconcat (base, "/", stream->control_url, NULL);
+
+ tried_non_compliant_url = TRUE;
+
+ goto retry;
+ }
+
+ /* fall through */
default:
/* cleanup of leftover transport and move to the next stream */
gst_rtspsrc_stream_free_udp (stream);
goto start_failed;
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
+ rtspsrc->seek_seqnum = gst_util_seqnum_next ();
/* init some state */
rtspsrc->cur_protocols = rtspsrc->protocols;
/* first attempt, don't ignore timeouts */
if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
if (rtspsrc->state >= GST_RTSP_STATE_READY) {
res = gst_rtspsrc_perform_seek (rtspsrc, event);
- gst_event_unref (event);
} else {
/* Store for later use */
res = TRUE;
- rtspsrc->initial_seek = event;
+ gst_event_replace (&rtspsrc->initial_seek, event);
}
+ gst_event_unref (event);
} else if (GST_EVENT_IS_DOWNSTREAM (event)) {
res = gst_rtspsrc_push_event (rtspsrc, event);
} else {