+
+/**
+ * gst_rtsp_stream_update_crypto:
+ * @stream: a #GstRTSPStream
+ * @ssrc: the SSRC
+ * @crypto: (transfer none) (allow-none): a #GstCaps with crypto info
+ *
+ * Update the new crypto information for @ssrc in @stream. If information
+ * for @ssrc did not exist, it will be added. If information
+ * for @ssrc existed, it will be replaced. If @crypto is %NULL, it will
+ * be removed from @stream.
+ *
+ * Returns: %TRUE if @crypto could be updated
+ */
+gboolean
+gst_rtsp_stream_update_crypto (GstRTSPStream * stream,
+ guint ssrc, GstCaps * crypto)
+{
+ GstRTSPStreamPrivate *priv;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+ g_return_val_if_fail (crypto == NULL || GST_IS_CAPS (crypto), FALSE);
+
+ priv = stream->priv;
+
+ GST_DEBUG_OBJECT (stream, "update key for %08x", ssrc);
+
+ g_mutex_lock (&priv->lock);
+ if (crypto)
+ g_hash_table_insert (priv->keys, GINT_TO_POINTER (ssrc),
+ gst_caps_ref (crypto));
+ else
+ g_hash_table_remove (priv->keys, GINT_TO_POINTER (ssrc));
+ g_mutex_unlock (&priv->lock);
+
+ return TRUE;
+}
+
+/**
+ * gst_rtsp_stream_get_rtp_socket:
+ * @stream: a #GstRTSPStream
+ * @family: the socket family
+ *
+ * Get the RTP socket from @stream for a @family.
+ *
+ * @stream must be joined to a bin.
+ *
+ * Returns: (transfer full) (nullable): the RTP socket or %NULL if no
+ * socket could be allocated for @family. Unref after usage
+ */
+GSocket *
+gst_rtsp_stream_get_rtp_socket (GstRTSPStream * stream, GSocketFamily family)
+{
+ GstRTSPStreamPrivate *priv = GST_RTSP_STREAM_GET_PRIVATE (stream);
+ GSocket *socket;
+ const gchar *name;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
+ g_return_val_if_fail (family == G_SOCKET_FAMILY_IPV4 ||
+ family == G_SOCKET_FAMILY_IPV6, NULL);
+ g_return_val_if_fail (priv->udpsink[0], NULL);
+
+ if (family == G_SOCKET_FAMILY_IPV6)
+ name = "socket-v6";
+ else
+ name = "socket";
+
+ g_object_get (priv->udpsink[0], name, &socket, NULL);
+
+ return socket;
+}
+
+/**
+ * gst_rtsp_stream_get_rtcp_socket:
+ * @stream: a #GstRTSPStream
+ * @family: the socket family
+ *
+ * Get the RTCP socket from @stream for a @family.
+ *
+ * @stream must be joined to a bin.
+ *
+ * Returns: (transfer full) (nullable): the RTCP socket or %NULL if no
+ * socket could be allocated for @family. Unref after usage
+ */
+GSocket *
+gst_rtsp_stream_get_rtcp_socket (GstRTSPStream * stream, GSocketFamily family)
+{
+ GstRTSPStreamPrivate *priv = GST_RTSP_STREAM_GET_PRIVATE (stream);
+ GSocket *socket;
+ const gchar *name;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
+ g_return_val_if_fail (family == G_SOCKET_FAMILY_IPV4 ||
+ family == G_SOCKET_FAMILY_IPV6, NULL);
+ g_return_val_if_fail (priv->udpsink[1], NULL);
+
+ if (family == G_SOCKET_FAMILY_IPV6)
+ name = "socket-v6";
+ else
+ name = "socket";
+
+ g_object_get (priv->udpsink[1], name, &socket, NULL);
+
+ return socket;
+}
+
+/**
+ * gst_rtsp_stream_set_seqnum:
+ * @stream: a #GstRTSPStream
+ * @seqnum: a new sequence number
+ *
+ * Configure the sequence number in the payloader of @stream to @seqnum.
+ */
+void
+gst_rtsp_stream_set_seqnum_offset (GstRTSPStream * stream, guint16 seqnum)
+{
+ GstRTSPStreamPrivate *priv;
+
+ g_return_if_fail (GST_IS_RTSP_STREAM (stream));
+
+ priv = stream->priv;
+
+ g_object_set (G_OBJECT (priv->payloader), "seqnum-offset", seqnum, NULL);
+}
+
+/**
+ * gst_rtsp_stream_get_seqnum:
+ * @stream: a #GstRTSPStream
+ *
+ * Get the configured sequence number in the payloader of @stream.
+ *
+ * Returns: the sequence number of the payloader.
+ */
+guint16
+gst_rtsp_stream_get_current_seqnum (GstRTSPStream * stream)
+{
+ GstRTSPStreamPrivate *priv;
+ guint seqnum;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), 0);
+
+ priv = stream->priv;
+
+ g_object_get (G_OBJECT (priv->payloader), "seqnum", &seqnum, NULL);
+
+ return seqnum;
+}
+
+/**
+ * gst_rtsp_stream_transport_filter:
+ * @stream: a #GstRTSPStream
+ * @func: (scope call) (allow-none): a callback
+ * @user_data: (closure): user data passed to @func
+ *
+ * Call @func for each transport managed by @stream. The result value of @func
+ * determines what happens to the transport. @func will be called with @stream
+ * locked so no further actions on @stream can be performed from @func.
+ *
+ * If @func returns #GST_RTSP_FILTER_REMOVE, the transport will be removed from
+ * @stream.
+ *
+ * If @func returns #GST_RTSP_FILTER_KEEP, the transport will remain in @stream.
+ *
+ * If @func returns #GST_RTSP_FILTER_REF, the transport will remain in @stream but
+ * will also be added with an additional ref to the result #GList of this
+ * function..
+ *
+ * When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for each transport.
+ *
+ * Returns: (element-type GstRTSPStreamTransport) (transfer full): a #GList with all
+ * transports for which @func returned #GST_RTSP_FILTER_REF. After usage, each
+ * element in the #GList should be unreffed before the list is freed.
+ */
+GList *
+gst_rtsp_stream_transport_filter (GstRTSPStream * stream,
+ GstRTSPStreamTransportFilterFunc func, gpointer user_data)
+{
+ GstRTSPStreamPrivate *priv;
+ GList *result, *walk, *next;
+ GHashTable *visited = NULL;
+ guint cookie;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
+
+ priv = stream->priv;
+
+ result = NULL;
+ if (func)
+ visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
+
+ g_mutex_lock (&priv->lock);
+restart:
+ cookie = priv->transports_cookie;
+ for (walk = priv->transports; walk; walk = next) {
+ GstRTSPStreamTransport *trans = walk->data;
+ GstRTSPFilterResult res;
+ gboolean changed;
+
+ next = g_list_next (walk);
+
+ if (func) {
+ /* only visit each transport once */
+ if (g_hash_table_contains (visited, trans))
+ continue;
+
+ g_hash_table_add (visited, g_object_ref (trans));
+ g_mutex_unlock (&priv->lock);
+
+ res = func (stream, trans, user_data);
+
+ g_mutex_lock (&priv->lock);
+ } else
+ res = GST_RTSP_FILTER_REF;
+
+ changed = (cookie != priv->transports_cookie);
+
+ switch (res) {
+ case GST_RTSP_FILTER_REMOVE:
+ update_transport (stream, trans, FALSE);
+ break;
+ case GST_RTSP_FILTER_REF:
+ result = g_list_prepend (result, g_object_ref (trans));
+ break;
+ case GST_RTSP_FILTER_KEEP:
+ default:
+ break;
+ }
+ if (changed)
+ goto restart;
+ }
+ g_mutex_unlock (&priv->lock);
+
+ if (func)
+ g_hash_table_unref (visited);
+
+ return result;
+}
+
+static GstPadProbeReturn
+pad_blocking (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
+{
+ GstRTSPStreamPrivate *priv;
+ GstRTSPStream *stream;
+
+ stream = user_data;
+ priv = stream->priv;
+
+ GST_DEBUG_OBJECT (pad, "now blocking");
+
+ g_mutex_lock (&priv->lock);
+ priv->blocking = TRUE;
+ g_mutex_unlock (&priv->lock);
+
+ gst_element_post_message (priv->payloader,
+ gst_message_new_element (GST_OBJECT_CAST (priv->payloader),
+ gst_structure_new_empty ("GstRTSPStreamBlocking")));
+
+ return GST_PAD_PROBE_OK;
+}
+
+/**
+ * gst_rtsp_stream_set_blocked:
+ * @stream: a #GstRTSPStream
+ * @blocked: boolean indicating we should block or unblock
+ *
+ * Blocks or unblocks the dataflow on @stream.
+ *
+ * Returns: %TRUE on success
+ */
+gboolean
+gst_rtsp_stream_set_blocked (GstRTSPStream * stream, gboolean blocked)
+{
+ GstRTSPStreamPrivate *priv;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+
+ priv = stream->priv;
+
+ g_mutex_lock (&priv->lock);
+ if (blocked) {
+ priv->blocking = FALSE;
+ if (priv->blocked_id == 0) {
+ priv->blocked_id = gst_pad_add_probe (priv->srcpad,
+ GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
+ GST_PAD_PROBE_TYPE_BUFFER_LIST, pad_blocking,
+ g_object_ref (stream), g_object_unref);
+ }
+ } else {
+ if (priv->blocked_id != 0) {
+ gst_pad_remove_probe (priv->srcpad, priv->blocked_id);
+ priv->blocked_id = 0;
+ priv->blocking = FALSE;
+ }
+ }
+ g_mutex_unlock (&priv->lock);
+
+ return TRUE;
+}
+
+/**
+ * gst_rtsp_stream_is_blocking:
+ * @stream: a #GstRTSPStream
+ *
+ * Check if @stream is blocking on a #GstBuffer.
+ *
+ * Returns: %TRUE if @stream is blocking
+ */
+gboolean
+gst_rtsp_stream_is_blocking (GstRTSPStream * stream)
+{
+ GstRTSPStreamPrivate *priv;
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+
+ priv = stream->priv;
+
+ g_mutex_lock (&priv->lock);
+ result = priv->blocking;
+ g_mutex_unlock (&priv->lock);
+
+ return result;
+}
+
+/**
+ * gst_rtsp_stream_query_position:
+ * @stream: a #GstRTSPStream
+ *
+ * Query the position of the stream in %GST_FORMAT_TIME. This only considers
+ * the RTP parts of the pipeline and not the RTCP parts.
+ *
+ * Returns: %TRUE if the position could be queried
+ */
+gboolean
+gst_rtsp_stream_query_position (GstRTSPStream * stream, gint64 * position)
+{
+ GstRTSPStreamPrivate *priv;
+ GstElement *sink;
+ gboolean ret;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+
+ priv = stream->priv;
+
+ g_mutex_lock (&priv->lock);
+ if ((sink = priv->udpsink[0]))
+ gst_object_ref (sink);
+ g_mutex_unlock (&priv->lock);
+
+ if (!sink)
+ return FALSE;
+
+ ret = gst_element_query_position (sink, GST_FORMAT_TIME, position);
+ gst_object_unref (sink);
+
+ return ret;
+}
+
+/**
+ * gst_rtsp_stream_query_stop:
+ * @stream: a #GstRTSPStream
+ *
+ * Query the stop of the stream in %GST_FORMAT_TIME. This only considers
+ * the RTP parts of the pipeline and not the RTCP parts.
+ *
+ * Returns: %TRUE if the stop could be queried
+ */
+gboolean
+gst_rtsp_stream_query_stop (GstRTSPStream * stream, gint64 * stop)
+{
+ GstRTSPStreamPrivate *priv;
+ GstElement *sink;
+ GstQuery *query;
+ gboolean ret;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+
+ priv = stream->priv;
+
+ g_mutex_lock (&priv->lock);
+ if ((sink = priv->udpsink[0]))
+ gst_object_ref (sink);
+ g_mutex_unlock (&priv->lock);
+
+ if (!sink)
+ return FALSE;
+
+ query = gst_query_new_segment (GST_FORMAT_TIME);
+ if ((ret = gst_element_query (sink, query))) {
+ GstFormat format;
+
+ gst_query_parse_segment (query, NULL, &format, NULL, stop);
+ if (format != GST_FORMAT_TIME)
+ *stop = -1;
+ }
+ gst_query_unref (query);
+ gst_object_unref (sink);
+
+ return ret;
+
+}