GstRTSPSession *session;
GstRTSPClientClass *klass;
GstRTSPSessionMedia *sessmedia;
+ GstRTSPMedia *media;
GstRTSPStatusCode code;
GstRTSPState rtspstate;
gchar *path;
gint matched;
+ guint i, n;
if (!(session = ctx->session))
goto no_session;
g_free (path);
+ media = gst_rtsp_session_media_get_media (sessmedia);
+ n = gst_rtsp_media_n_streams (media);
+ for (i = 0; i < n; i++) {
+ GstRTSPStream *stream = gst_rtsp_media_get_stream (media, i);
+
+ if (gst_rtsp_stream_get_publish_clock_mode (stream) ==
+ GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET)
+ goto not_supported;
+ }
+
ctx->sessmedia = sessmedia;
rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
ctx);
return FALSE;
}
+not_supported:
+ {
+ GST_ERROR ("client %p: pausing not supported", client);
+ send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
+ return FALSE;
+ }
}
/* convert @url and @path to a URL used as a content base for the factory
GType media_gtype;
GstClock *clock;
+
+ GstRTSPPublishClockMode publish_clock_mode;
};
#define DEFAULT_LAUNCH NULL
priv->latency = DEFAULT_LATENCY;
priv->transport_mode = DEFAULT_TRANSPORT_MODE;
priv->stop_on_disconnect = DEFAULT_STOP_ON_DISCONNECT;
+ priv->publish_clock_mode = GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK;
g_mutex_init (&priv->lock);
g_mutex_init (&priv->medias_lock);
return ret;
}
+/**
+ * gst_rtsp_media_factory_set_publish_clock_mode:
+ * @factory: a #GstRTSPMediaFactory
+ * @mode: the clock publish mode
+ *
+ * Sets if and how the media clock should be published according to RFC7273.
+ *
+ * Since: 1.8
+ */
+void
+gst_rtsp_media_factory_set_publish_clock_mode (GstRTSPMediaFactory * factory,
+ GstRTSPPublishClockMode mode)
+{
+ GstRTSPMediaFactoryPrivate *priv;
+
+ GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+ priv = factory->priv;
+ priv->publish_clock_mode = mode;
+ GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+}
+
+/**
+ * gst_rtsp_media_factory_get_publish_clock_mode:
+ * @factory: a #GstRTSPMediaFactory
+ *
+ * Gets if and how the media clock should be published according to RFC7273.
+ *
+ * Returns: The GstRTSPPublishClockMode
+ *
+ * Since: 1.8
+ */
+GstRTSPPublishClockMode
+gst_rtsp_media_factory_get_publish_clock_mode (GstRTSPMediaFactory * factory)
+{
+ GstRTSPMediaFactoryPrivate *priv;
+ GstRTSPPublishClockMode ret;
+
+ GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+ priv = factory->priv;
+ ret = priv->publish_clock_mode;
+ GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+
+ return ret;
+}
+
static gchar *
default_gen_key (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
{
GstRTSPTransportMode transport_mode;
GstClock *clock;
gchar *multicast_iface;
+ GstRTSPPublishClockMode publish_clock_mode;
/* configure the sharedness */
GST_RTSP_MEDIA_FACTORY_LOCK (factory);
transport_mode = priv->transport_mode;
stop_on_disconnect = priv->stop_on_disconnect;
clock = priv->clock ? gst_object_ref (priv->clock) : NULL;
+ publish_clock_mode = priv->publish_clock_mode;
GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
gst_rtsp_media_set_suspend_mode (media, suspend_mode);
gst_rtsp_media_set_latency (media, latency);
gst_rtsp_media_set_transport_mode (media, transport_mode);
gst_rtsp_media_set_stop_on_disconnect (media, stop_on_disconnect);
+ gst_rtsp_media_set_publish_clock_mode (media, publish_clock_mode);
if (clock) {
gst_rtsp_media_set_clock (media, clock);
GstClock * clock);
GstClock * gst_rtsp_media_factory_get_clock (GstRTSPMediaFactory *factory);
+void gst_rtsp_media_factory_set_publish_clock_mode (GstRTSPMediaFactory * factory, GstRTSPPublishClockMode mode);
+GstRTSPPublishClockMode gst_rtsp_media_factory_get_publish_clock_mode (GstRTSPMediaFactory * factory);
+
/* creating the media from the factory and a url */
GstRTSPMedia * gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory,
const GstRTSPUrl *url);
GstClockTime rtx_time; /* protected by lock */
guint latency; /* protected by lock */
GstClock *clock; /* protected by lock */
+ GstRTSPPublishClockMode publish_clock_mode;
};
#define DEFAULT_SHARED FALSE
return (GType) id;
}
+GType
+gst_rtsp_publish_clock_mode_get_type (void)
+{
+ static gsize id = 0;
+ static const GEnumValue values[] = {
+ {C_ENUM (GST_RTSP_PUBLISH_CLOCK_MODE_NONE),
+ "GST_RTSP_PUBLISH_CLOCK_MODE_NONE", "none"},
+ {C_ENUM (GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK),
+ "GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK",
+ "clock"},
+ {C_ENUM (GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET),
+ "GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET",
+ "clock-and-offset"},
+ {0, NULL, NULL}
+ };
+
+ if (g_once_init_enter (&id)) {
+ GType tmp = g_enum_register_static ("GstRTSPPublishClockMode", values);
+ g_once_init_leave (&id, tmp);
+ }
+ return (GType) id;
+}
+
G_DEFINE_TYPE (GstRTSPMedia, gst_rtsp_media, G_TYPE_OBJECT);
static void
priv->time_provider = DEFAULT_TIME_PROVIDER;
priv->transport_mode = DEFAULT_TRANSPORT_MODE;
priv->stop_on_disconnect = DEFAULT_STOP_ON_DISCONNECT;
+ priv->publish_clock_mode = GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK;
}
static void
}
/**
+ * gst_rtsp_media_set_publish_clock_mode:
+ * @media: a #GstRTSPMedia
+ * @mode: the clock publish mode
+ *
+ * Sets if and how the media clock should be published according to RFC7273.
+ *
+ * Since: 1.8
+ */
+void
+gst_rtsp_media_set_publish_clock_mode (GstRTSPMedia * media,
+ GstRTSPPublishClockMode mode)
+{
+ GstRTSPMediaPrivate *priv;
+ guint i, n;
+
+ priv = media->priv;
+ g_mutex_lock (&priv->lock);
+ priv->publish_clock_mode = mode;
+
+ n = priv->streams->len;
+ for (i = 0; i < n; i++) {
+ GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
+
+ gst_rtsp_stream_set_publish_clock_mode (stream, mode);
+ }
+ g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_media_get_publish_clock_mode:
+ * @factory: a #GstRTSPMedia
+ *
+ * Gets if and how the media clock should be published according to RFC7273.
+ *
+ * Returns: The GstRTSPPublishClockMode
+ *
+ * Since: 1.8
+ */
+GstRTSPPublishClockMode
+gst_rtsp_media_get_publish_clock_mode (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv;
+ GstRTSPPublishClockMode ret;
+
+ priv = media->priv;
+ g_mutex_lock (&priv->lock);
+ ret = priv->publish_clock_mode;
+ g_mutex_unlock (&priv->lock);
+
+ return ret;
+}
+
+/**
* gst_rtsp_media_set_address_pool:
* @media: a #GstRTSPMedia
* @pool: (transfer none): a #GstRTSPAddressPool
gst_rtsp_stream_set_protocols (stream, priv->protocols);
gst_rtsp_stream_set_retransmission_time (stream, priv->rtx_time);
gst_rtsp_stream_set_buffer_size (stream, priv->buffer_size);
+ gst_rtsp_stream_set_publish_clock_mode (stream, priv->publish_clock_mode);
g_ptr_array_add (priv->streams, stream);
/* TODO: Seeking for RECORD? */
priv->seekable = FALSE;
} else {
+ guint i, n = priv->streams->len;
+
+ for (i = 0; i < n; i++) {
+ GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
+
+ if (gst_rtsp_stream_get_publish_clock_mode (stream) ==
+ GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET) {
+ priv->seekable = FALSE;
+ goto not_seekable;
+ }
+ }
+
query = gst_query_new_seeking (GST_FORMAT_TIME);
if (gst_element_query (priv->pipeline, query)) {
GstFormat format;
gst_query_parse_seeking (query, &format, &seekable, &start, &end);
priv->seekable = seekable;
}
+
gst_query_unref (query);
}
GST_RTSP_TRANSPORT_MODE_RECORD = 2,
} GstRTSPTransportMode;
+/**
+ * GstRTSPPublishClockMode:
+ * @GST_RTSP_PUBLISH_CLOCK_MODE_NONE: Publish nothing
+ * @GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK: Publish the clock but not the offset
+ * @GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET: Publish the clock and offset
+ *
+ * Whether the clock and possibly RTP/clock offset should be published according to RFC7273.
+ */
+typedef enum {
+ GST_RTSP_PUBLISH_CLOCK_MODE_NONE,
+ GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK,
+ GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET
+} GstRTSPPublishClockMode;
+
#define GST_TYPE_RTSP_TRANSPORT_MODE (gst_rtsp_transport_mode_get_type())
GType gst_rtsp_transport_mode_get_type (void);
#define GST_TYPE_RTSP_SUSPEND_MODE (gst_rtsp_suspend_mode_get_type())
GType gst_rtsp_suspend_mode_get_type (void);
+#define GST_TYPE_RTSP_PUBLISH_CLOCK_MODE (gst_rtsp_publish_clock_mode_get_type())
+GType gst_rtsp_publish_clock_mode_get_type (void);
+
#include "rtsp-stream.h"
#include "rtsp-thread-pool.h"
#include "rtsp-permissions.h"
void gst_rtsp_media_set_clock (GstRTSPMedia *media, GstClock * clock);
+
+void gst_rtsp_media_set_publish_clock_mode (GstRTSPMedia * media, GstRTSPPublishClockMode mode);
+GstRTSPPublishClockMode gst_rtsp_media_get_publish_clock_mode (GstRTSPMedia * media);
+
/* prepare the media for playback */
gboolean gst_rtsp_media_prepare (GstRTSPMedia *media, GstRTSPThread *thread);
gboolean gst_rtsp_media_unprepare (GstRTSPMedia *media);
#include <string.h>
+#include <gst/net/net.h>
#include <gst/sdp/gstmikey.h>
#include "rtsp-sdp.h"
gst_mikey_message_unref (mikey_msg);
}
+ /* RFC 7273 clock signalling */
+ {
+ GstBin *joined_bin = gst_rtsp_stream_get_joined_bin (stream);
+ GstClock *clock = gst_element_get_clock (GST_ELEMENT_CAST (joined_bin));
+ gchar *ts_refclk = NULL;
+ gchar *mediaclk = NULL;
+ guint rtptime, clock_rate;
+ GstClockTime running_time, base_time, clock_time;
+ GstRTSPPublishClockMode publish_clock_mode =
+ gst_rtsp_stream_get_publish_clock_mode (stream);
+
+ gst_rtsp_stream_get_rtpinfo (stream, &rtptime, NULL, &clock_rate,
+ &running_time);
+ base_time = gst_element_get_base_time (GST_ELEMENT_CAST (joined_bin));
+ g_assert (base_time != GST_CLOCK_TIME_NONE);
+ clock_time = running_time + base_time;
+
+ if (publish_clock_mode != GST_RTSP_PUBLISH_CLOCK_MODE_NONE && clock) {
+ if (GST_IS_NTP_CLOCK (clock) || GST_IS_PTP_CLOCK (clock)) {
+ if (publish_clock_mode == GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET) {
+ guint32 mediaclk_offset;
+
+ /* Calculate RTP time at the clock's epoch. That's the direct offset */
+ clock_time =
+ gst_util_uint64_scale (clock_time, clock_rate, GST_SECOND);
+
+ clock_time &= 0xffffffff;
+ mediaclk_offset =
+ G_GUINT64_CONSTANT (0xffffffff) + rtptime - clock_time;
+ mediaclk = g_strdup_printf ("direct=%u", (guint32) mediaclk_offset);
+ }
+
+ if (GST_IS_NTP_CLOCK (clock)) {
+ gchar *ntp_address;
+ guint ntp_port;
+
+ g_object_get (clock, "address", &ntp_address, "port", &ntp_port,
+ NULL);
+
+ if (ntp_port == 123)
+ ts_refclk = g_strdup_printf ("ntp=%s", ntp_address);
+ else
+ ts_refclk = g_strdup_printf ("ntp=%s:%u", ntp_address, ntp_port);
+
+ g_free (ntp_address);
+ } else {
+ guint64 ptp_clock_id;
+ guint ptp_domain;
+
+ g_object_get (clock, "grandmaster-clock-id", &ptp_clock_id, "domain",
+ &ptp_domain, NULL);
+
+ if (ptp_domain != 0)
+ ts_refclk =
+ g_strdup_printf
+ ("ptp=IEEE1588-2008:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X:%u",
+ (guint) (ptp_clock_id >> 56) & 0xff,
+ (guint) (ptp_clock_id >> 48) & 0xff,
+ (guint) (ptp_clock_id >> 40) & 0xff,
+ (guint) (ptp_clock_id >> 32) & 0xff,
+ (guint) (ptp_clock_id >> 24) & 0xff,
+ (guint) (ptp_clock_id >> 16) & 0xff,
+ (guint) (ptp_clock_id >> 8) & 0xff,
+ (guint) (ptp_clock_id >> 0) & 0xff, ptp_domain);
+ else
+ ts_refclk =
+ g_strdup_printf
+ ("ptp=IEEE1588-2008:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
+ (guint) (ptp_clock_id >> 56) & 0xff,
+ (guint) (ptp_clock_id >> 48) & 0xff,
+ (guint) (ptp_clock_id >> 40) & 0xff,
+ (guint) (ptp_clock_id >> 32) & 0xff,
+ (guint) (ptp_clock_id >> 24) & 0xff,
+ (guint) (ptp_clock_id >> 16) & 0xff,
+ (guint) (ptp_clock_id >> 8) & 0xff,
+ (guint) (ptp_clock_id >> 0) & 0xff);
+ }
+ }
+ }
+ if (clock)
+ gst_object_unref (clock);
+
+ if (!ts_refclk)
+ ts_refclk = g_strdup ("local");
+ if (!mediaclk)
+ mediaclk = g_strdup ("sender");
+
+ gst_sdp_media_add_attribute (smedia, "ts-refclk", ts_refclk);
+ gst_sdp_media_add_attribute (smedia, "mediaclk", mediaclk);
+ g_free (ts_refclk);
+ g_free (mediaclk);
+ gst_object_unref (joined_bin);
+ }
+
update_sdp_from_tags (stream, smedia);
if ((profile == GST_RTSP_PROFILE_AVPF || profile == GST_RTSP_PROFILE_SAVPF)
GstElement *payloader;
guint buffer_size;
gboolean is_joined;
+ GstBin *joined_bin;
/* TRUE if this stream is running on
* the client side of an RTSP link (for RECORD) */
/* pt->caps map for RECORD streams */
GHashTable *ptmap;
+
+ GstRTSPPublishClockMode publish_clock_mode;
};
#define DEFAULT_CONTROL NULL
priv->control = g_strdup (DEFAULT_CONTROL);
priv->profiles = DEFAULT_PROFILES;
priv->protocols = DEFAULT_PROTOCOLS;
+ priv->publish_clock_mode = GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK;
g_mutex_init (&priv->lock);
g_mutex_unlock (&priv->lock);
}
+/**
+ * gst_rtsp_stream_set_publish_clock_mode:
+ * @stream: a #GstRTSPStream
+ * @mode: the clock publish mode
+ *
+ * Sets if and how the stream clock should be published according to RFC7273.
+ *
+ * Since: 1.8
+ */
+void
+gst_rtsp_stream_set_publish_clock_mode (GstRTSPStream * stream,
+ GstRTSPPublishClockMode mode)
+{
+ GstRTSPStreamPrivate *priv;
+
+ priv = stream->priv;
+ g_mutex_lock (&priv->lock);
+ priv->publish_clock_mode = mode;
+ g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_stream_get_publish_clock_mode:
+ * @factory: a #GstRTSPStream
+ *
+ * Gets if and how the stream clock should be published according to RFC7273.
+ *
+ * Returns: The GstRTSPPublishClockMode
+ *
+ * Since: 1.8
+ */
+GstRTSPPublishClockMode
+gst_rtsp_stream_get_publish_clock_mode (GstRTSPStream * stream)
+{
+ GstRTSPStreamPrivate *priv;
+ GstRTSPPublishClockMode ret;
+
+ priv = stream->priv;
+ g_mutex_lock (&priv->lock);
+ ret = priv->publish_clock_mode;
+ g_mutex_unlock (&priv->lock);
+
+ return ret;
+}
+
static GstCaps *
request_pt_map (GstElement * rtpbin, guint session, guint pt,
GstRTSPStream * stream)
(GCallback) caps_notify, stream);
}
+ priv->joined_bin = bin;
priv->is_joined = TRUE;
g_mutex_unlock (&priv->lock);
if (!priv->is_joined)
goto was_not_joined;
+ priv->joined_bin = NULL;
+
/* all transports must be removed by now */
if (priv->transports != NULL)
goto transports_not_removed;
}
/**
+ * gst_rtsp_stream_get_joined_bin:
+ * @stream: a #GstRTSPStream
+ *
+ * Get the previous joined bin with gst_rtsp_stream_join_bin() or NULL.
+ *
+ * Return: (transfer full): the joined bin or NULL.
+ */
+GstBin *
+gst_rtsp_stream_get_joined_bin (GstRTSPStream * stream)
+{
+ GstRTSPStreamPrivate *priv;
+ GstBin *bin = NULL;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+
+ priv = stream->priv;
+
+ g_mutex_lock (&priv->lock);
+ bin = priv->joined_bin ? gst_object_ref (priv->joined_bin) : NULL;
+ g_mutex_unlock (&priv->lock);
+
+ return bin;
+}
+
+/**
* gst_rtsp_stream_get_rtpinfo:
* @stream: a #GstRTSPStream
* @rtptime: (allow-none): result RTP timestamp
#include "rtsp-stream-transport.h"
#include "rtsp-address-pool.h"
#include "rtsp-session.h"
+#include "rtsp-media.h"
/**
* GstRTSPStream:
GstState state);
gboolean gst_rtsp_stream_leave_bin (GstRTSPStream *stream,
GstBin *bin, GstElement *rtpbin);
+GstBin * gst_rtsp_stream_get_joined_bin (GstRTSPStream *stream);
gboolean gst_rtsp_stream_set_blocked (GstRTSPStream * stream,
gboolean blocked);
gboolean gst_rtsp_stream_allocate_udp_sockets (GstRTSPStream * stream, GSocketFamily family,
GstRTSPTransport *transport, gboolean use_client_setttings);
+
+void gst_rtsp_stream_set_publish_clock_mode (GstRTSPStream * stream, GstRTSPPublishClockMode mode);
+GstRTSPPublishClockMode gst_rtsp_stream_get_publish_clock_mode (GstRTSPStream * stream);
+
/**
* GstRTSPStreamTransportFilterFunc:
* @stream: a #GstRTSPStream object