+/* should be called with state-lock */
+static GstClock *
+get_clock_unlocked (GstRTSPMedia * media)
+{
+ if (media->priv->status != GST_RTSP_MEDIA_STATUS_PREPARED) {
+ GST_DEBUG_OBJECT (media, "media was not prepared");
+ return NULL;
+ }
+ return gst_pipeline_get_clock (GST_PIPELINE_CAST (media->priv->pipeline));
+}
+
+/**
+ * gst_rtsp_media_get_clock:
+ * @media: a #GstRTSPMedia
+ *
+ * Get the clock that is used by the pipeline in @media.
+ *
+ * @media must be prepared before this method returns a valid clock object.
+ *
+ * Returns: (transfer full): the #GstClock used by @media. unref after usage.
+ */
+GstClock *
+gst_rtsp_media_get_clock (GstRTSPMedia * media)
+{
+ GstClock *clock;
+ GstRTSPMediaPrivate *priv;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
+
+ priv = media->priv;
+
+ g_rec_mutex_lock (&priv->state_lock);
+ clock = get_clock_unlocked (media);
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ return clock;
+}
+
+/**
+ * gst_rtsp_media_get_base_time:
+ * @media: a #GstRTSPMedia
+ *
+ * Get the base_time that is used by the pipeline in @media.
+ *
+ * @media must be prepared before this method returns a valid base_time.
+ *
+ * Returns: the base_time used by @media.
+ */
+GstClockTime
+gst_rtsp_media_get_base_time (GstRTSPMedia * media)
+{
+ GstClockTime result;
+ GstRTSPMediaPrivate *priv;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_CLOCK_TIME_NONE);
+
+ priv = media->priv;
+
+ g_rec_mutex_lock (&priv->state_lock);
+ if (media->priv->status != GST_RTSP_MEDIA_STATUS_PREPARED)
+ goto not_prepared;
+
+ result = gst_element_get_base_time (media->priv->pipeline);
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ return result;
+
+ /* ERRORS */
+not_prepared:
+ {
+ g_rec_mutex_unlock (&priv->state_lock);
+ GST_DEBUG_OBJECT (media, "media was not prepared");
+ return GST_CLOCK_TIME_NONE;
+ }
+}
+
+/**
+ * gst_rtsp_media_get_time_provider:
+ * @media: a #GstRTSPMedia
+ * @address: (allow-none): an address or %NULL
+ * @port: a port or 0
+ *
+ * Get the #GstNetTimeProvider for the clock used by @media. The time provider
+ * will listen on @address and @port for client time requests.
+ *
+ * Returns: (transfer full): the #GstNetTimeProvider of @media.
+ */
+GstNetTimeProvider *
+gst_rtsp_media_get_time_provider (GstRTSPMedia * media, const gchar * address,
+ guint16 port)
+{
+ GstRTSPMediaPrivate *priv;
+ GstNetTimeProvider *provider = NULL;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
+
+ priv = media->priv;
+
+ g_rec_mutex_lock (&priv->state_lock);
+ if (priv->time_provider) {
+ if ((provider = priv->nettime) == NULL) {
+ GstClock *clock;
+
+ if (priv->time_provider && (clock = get_clock_unlocked (media))) {
+ provider = gst_net_time_provider_new (clock, address, port);
+ gst_object_unref (clock);
+
+ priv->nettime = provider;
+ }
+ }
+ }
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ if (provider)
+ gst_object_ref (provider);
+
+ return provider;
+}
+
+static gboolean
+default_setup_sdp (GstRTSPMedia * media, GstSDPMessage * sdp, GstSDPInfo * info)
+{
+ return gst_rtsp_sdp_from_media (sdp, info, media);
+}
+
+/**
+ * gst_rtsp_media_setup_sdp:
+ * @media: a #GstRTSPMedia
+ * @sdp: (transfer none): a #GstSDPMessage
+ * @info: (transfer none): a #GstSDPInfo
+ *
+ * Add @media specific info to @sdp. @info is used to configure the connection
+ * information in the SDP.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gst_rtsp_media_setup_sdp (GstRTSPMedia * media, GstSDPMessage * sdp,
+ GstSDPInfo * info)
+{
+ GstRTSPMediaPrivate *priv;
+ GstRTSPMediaClass *klass;
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+ g_return_val_if_fail (sdp != NULL, FALSE);
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ priv = media->priv;
+
+ g_rec_mutex_lock (&priv->state_lock);
+
+ klass = GST_RTSP_MEDIA_GET_CLASS (media);
+
+ if (!klass->setup_sdp)
+ goto no_setup_sdp;
+
+ res = klass->setup_sdp (media, sdp, info);
+
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ return res;
+
+ /* ERRORS */
+no_setup_sdp:
+ {
+ g_rec_mutex_unlock (&priv->state_lock);
+ GST_ERROR ("no setup_sdp function");
+ g_critical ("no setup_sdp vmethod function set");
+ return FALSE;
+ }
+}
+
+static void
+do_set_seqnum (GstRTSPStream * stream)
+{
+ guint16 seq_num;
+ seq_num = gst_rtsp_stream_get_current_seqnum (stream);
+ gst_rtsp_stream_set_seqnum_offset (stream, seq_num + 1);
+}
+
+/* call with state_lock */
+gboolean
+default_suspend (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+ GstStateChangeReturn ret;
+
+ switch (priv->suspend_mode) {
+ case GST_RTSP_SUSPEND_MODE_NONE:
+ GST_DEBUG ("media %p no suspend", media);
+ break;
+ case GST_RTSP_SUSPEND_MODE_PAUSE:
+ GST_DEBUG ("media %p suspend to PAUSED", media);
+ ret = set_target_state (media, GST_STATE_PAUSED, TRUE);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto state_failed;
+ break;
+ case GST_RTSP_SUSPEND_MODE_RESET:
+ GST_DEBUG ("media %p suspend to NULL", media);
+ ret = set_target_state (media, GST_STATE_NULL, TRUE);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto state_failed;
+ /* Because payloader needs to set the sequence number as
+ * monotonic, we need to preserve the sequence number
+ * after pause. (otherwise going from pause to play, which
+ * is actually from NULL to PLAY will create a new sequence
+ * number. */
+ g_ptr_array_foreach (priv->streams, (GFunc) do_set_seqnum, NULL);
+ break;
+ default:
+ break;
+ }
+
+ /* let the streams do the state changes freely, if any */
+ media_streams_set_blocked (media, FALSE);
+
+ return TRUE;
+
+ /* ERRORS */
+state_failed:
+ {
+ GST_WARNING ("failed changing pipeline's state for media %p", media);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_rtsp_media_suspend:
+ * @media: a #GstRTSPMedia
+ *
+ * Suspend @media. The state of the pipeline managed by @media is set to
+ * GST_STATE_NULL but all streams are kept. @media can be prepared again
+ * with gst_rtsp_media_unsuspend()
+ *
+ * @media must be prepared with gst_rtsp_media_prepare();
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+gst_rtsp_media_suspend (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+ GstRTSPMediaClass *klass;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+
+ GST_FIXME ("suspend for dynamic pipelines needs fixing");
+
+ g_rec_mutex_lock (&priv->state_lock);
+ if (priv->status != GST_RTSP_MEDIA_STATUS_PREPARED)
+ goto not_prepared;
+
+ /* don't attempt to suspend when something is busy */
+ if (priv->n_active > 0)
+ goto done;
+
+ klass = GST_RTSP_MEDIA_GET_CLASS (media);
+ if (klass->suspend) {
+ if (!klass->suspend (media))
+ goto suspend_failed;
+ }
+
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_SUSPENDED);
+done:
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ return TRUE;
+
+ /* ERRORS */
+not_prepared:
+ {
+ g_rec_mutex_unlock (&priv->state_lock);
+ GST_WARNING ("media %p was not prepared", media);
+ return FALSE;
+ }
+suspend_failed:
+ {
+ g_rec_mutex_unlock (&priv->state_lock);
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_ERROR);
+ GST_WARNING ("failed to suspend media %p", media);
+ return FALSE;
+ }
+}
+
+/* call with state_lock */
+gboolean
+default_unsuspend (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+
+ switch (priv->suspend_mode) {
+ case GST_RTSP_SUSPEND_MODE_NONE:
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARED);
+ break;
+ case GST_RTSP_SUSPEND_MODE_PAUSE:
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARED);
+ break;
+ case GST_RTSP_SUSPEND_MODE_RESET:
+ {
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
+ if (!start_preroll (media))
+ goto start_failed;
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ if (!wait_preroll (media))
+ goto preroll_failed;
+
+ g_rec_mutex_lock (&priv->state_lock);
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+
+ /* ERRORS */
+start_failed:
+ {
+ GST_WARNING ("failed to preroll pipeline");
+ return FALSE;
+ }
+preroll_failed:
+ {
+ GST_WARNING ("failed to preroll pipeline");
+ return FALSE;
+ }
+}
+
+/**
+ * gst_rtsp_media_unsuspend:
+ * @media: a #GstRTSPMedia
+ *
+ * Unsuspend @media if it was in a suspended state. This method does nothing
+ * when the media was not in the suspended state.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+gst_rtsp_media_unsuspend (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+ GstRTSPMediaClass *klass;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+
+ g_rec_mutex_lock (&priv->state_lock);
+ if (priv->status != GST_RTSP_MEDIA_STATUS_SUSPENDED)
+ goto done;
+
+ klass = GST_RTSP_MEDIA_GET_CLASS (media);
+ if (klass->unsuspend) {
+ if (!klass->unsuspend (media))
+ goto unsuspend_failed;
+ }
+
+done:
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ return TRUE;
+
+ /* ERRORS */
+unsuspend_failed:
+ {
+ g_rec_mutex_unlock (&priv->state_lock);
+ GST_WARNING ("failed to unsuspend media %p", media);
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_ERROR);
+ return FALSE;
+ }
+}
+
+/* must be called with state-lock */
+static void
+media_set_pipeline_state_locked (GstRTSPMedia * media, GstState state)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+
+ if (state == GST_STATE_NULL) {
+ gst_rtsp_media_unprepare (media);
+ } else {
+ GST_INFO ("state %s media %p", gst_element_state_get_name (state), media);
+ set_target_state (media, state, FALSE);
+ /* when we are buffering, don't update the state yet, this will be done
+ * when buffering finishes */
+ if (priv->buffering) {
+ GST_INFO ("Buffering busy, delay state change");
+ } else {
+ if (state == GST_STATE_PLAYING)
+ /* make sure pads are not blocking anymore when going to PLAYING */
+ media_streams_set_blocked (media, FALSE);
+
+ set_state (media, state);
+
+ /* and suspend after pause */
+ if (state == GST_STATE_PAUSED)
+ gst_rtsp_media_suspend (media);
+ }
+ }
+}
+
+/**
+ * gst_rtsp_media_set_pipeline_state:
+ * @media: a #GstRTSPMedia
+ * @state: the target state of the pipeline
+ *
+ * Set the state of the pipeline managed by @media to @state
+ */
+void
+gst_rtsp_media_set_pipeline_state (GstRTSPMedia * media, GstState state)
+{
+ g_return_if_fail (GST_IS_RTSP_MEDIA (media));
+
+ g_rec_mutex_lock (&media->priv->state_lock);
+ media_set_pipeline_state_locked (media, state);
+ g_rec_mutex_unlock (&media->priv->state_lock);
+}
+