summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wtaymans@redhat.com>2013-11-28 13:47:07 +0100
committerWim Taymans <wtaymans@redhat.com>2013-11-28 13:47:07 +0100
commit0024b736dbbcc990b52c0520e78173589485a85d (patch)
tree3a6cb2afeac94e8fa515304ffc6760d833e114cb
parentb63c8356ce368e7573f37d455bc8472e3702767a (diff)
-rw-r--r--examples/test-readme.c4
-rw-r--r--gst/rtsp-server/rtsp-client.c13
-rw-r--r--gst/rtsp-server/rtsp-media-factory.c68
-rw-r--r--gst/rtsp-server/rtsp-media-factory.h4
-rw-r--r--gst/rtsp-server/rtsp-media.c422
-rw-r--r--gst/rtsp-server/rtsp-media.h38
-rw-r--r--gst/rtsp-server/rtsp-session-media.c6
-rw-r--r--gst/rtsp-server/rtsp-session.c6
-rw-r--r--tests/check/gst/media.c45
-rw-r--r--tests/check/gst/mediafactory.c35
10 files changed, 592 insertions, 49 deletions
diff --git a/examples/test-readme.c b/examples/test-readme.c
index 2e2caa6..6f97e4f 100644
--- a/examples/test-readme.c
+++ b/examples/test-readme.c
@@ -45,8 +45,10 @@ main (int argc, char *argv[])
* any launch line works as long as it contains elements named pay%d. Each
* element with pay%d names will be a stream */
factory = gst_rtsp_media_factory_new ();
+ gst_rtsp_media_factory_set_suspend_mode (factory,
+ GST_RTSP_SUSPEND_MODE_RESET);
gst_rtsp_media_factory_set_launch (factory,
- "( videotestsrc is-live=1 ! x264enc ! rtph264pay name=pay0 pt=96 )");
+ "( videotestsrc is-live=1 ! x264enc tune=lowlatency ! rtph264pay name=pay0 pt=96 )");
gst_rtsp_media_factory_set_shared (factory, TRUE);
diff --git a/gst/rtsp-server/rtsp-client.c b/gst/rtsp-server/rtsp-client.c
index 76f8c8c..162f52c 100644
--- a/gst/rtsp-server/rtsp-client.c
+++ b/gst/rtsp-server/rtsp-client.c
@@ -1074,6 +1074,10 @@ handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
if (rtspstate != GST_RTSP_STATE_PLAYING && rtspstate != GST_RTSP_STATE_READY)
goto invalid_state;
+ /* in play we first unsuspend, media could be suspended from SDP or PAUSED */
+ if (!gst_rtsp_media_unsuspend (media))
+ goto unsuspend_failed;
+
/* parse the range header if we have one */
res = gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RANGE, &str, 0);
if (res == GST_RTSP_OK) {
@@ -1194,6 +1198,13 @@ invalid_state:
g_free (path);
return FALSE;
}
+unsuspend_failed:
+ {
+ GST_ERROR ("client %p: unsuspend failed", client);
+ send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
+ g_free (path);
+ return FALSE;
+ }
}
static void
@@ -1755,6 +1766,8 @@ handle_describe_request (GstRTSPClient * client, GstRTSPContext * ctx)
if (!(sdp = klass->create_sdp (client, media)))
goto no_sdp;
+ /* we suspend after the describe */
+ gst_rtsp_media_suspend (media);
g_object_unref (media);
gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
diff --git a/gst/rtsp-server/rtsp-media-factory.c b/gst/rtsp-server/rtsp-media-factory.c
index d04c638..590b1f7 100644
--- a/gst/rtsp-server/rtsp-media-factory.c
+++ b/gst/rtsp-server/rtsp-media-factory.c
@@ -51,6 +51,7 @@ struct _GstRTSPMediaFactoryPrivate
GstRTSPPermissions *permissions;
gchar *launch;
gboolean shared;
+ GstRTSPSuspendMode suspend_mode;
gboolean eos_shutdown;
GstRTSPLowerTrans protocols;
guint buffer_size;
@@ -62,6 +63,7 @@ struct _GstRTSPMediaFactoryPrivate
#define DEFAULT_LAUNCH NULL
#define DEFAULT_SHARED FALSE
+#define DEFAULT_SUSPEND_MODE GST_RTSP_SUSPEND_MODE_NONE
#define DEFAULT_EOS_SHUTDOWN FALSE
#define DEFAULT_PROTOCOLS GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST | \
GST_RTSP_LOWER_TRANS_TCP
@@ -72,6 +74,7 @@ enum
PROP_0,
PROP_LAUNCH,
PROP_SHARED,
+ PROP_SUSPEND_MODE,
PROP_EOS_SHUTDOWN,
PROP_PROTOCOLS,
PROP_BUFFER_SIZE,
@@ -148,6 +151,11 @@ gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass)
"If media from this factory is shared", DEFAULT_SHARED,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_SUSPEND_MODE,
+ g_param_spec_enum ("suspend-mode", "Suspend Mode",
+ "Control how media will be suspended", GST_TYPE_RTSP_SUSPEND_MODE,
+ DEFAULT_SUSPEND_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
g_object_class_install_property (gobject_class, PROP_EOS_SHUTDOWN,
g_param_spec_boolean ("eos-shutdown", "EOS Shutdown",
"Send EOS down the pipeline before shutting down",
@@ -194,6 +202,7 @@ gst_rtsp_media_factory_init (GstRTSPMediaFactory * factory)
priv->launch = g_strdup (DEFAULT_LAUNCH);
priv->shared = DEFAULT_SHARED;
+ priv->suspend_mode = DEFAULT_SUSPEND_MODE;
priv->eos_shutdown = DEFAULT_EOS_SHUTDOWN;
priv->protocols = DEFAULT_PROTOCOLS;
priv->buffer_size = DEFAULT_BUFFER_SIZE;
@@ -235,6 +244,10 @@ gst_rtsp_media_factory_get_property (GObject * object, guint propid,
case PROP_SHARED:
g_value_set_boolean (value, gst_rtsp_media_factory_is_shared (factory));
break;
+ case PROP_SUSPEND_MODE:
+ g_value_set_enum (value,
+ gst_rtsp_media_factory_get_suspend_mode (factory));
+ break;
case PROP_EOS_SHUTDOWN:
g_value_set_boolean (value,
gst_rtsp_media_factory_is_eos_shutdown (factory));
@@ -264,6 +277,10 @@ gst_rtsp_media_factory_set_property (GObject * object, guint propid,
case PROP_SHARED:
gst_rtsp_media_factory_set_shared (factory, g_value_get_boolean (value));
break;
+ case PROP_SUSPEND_MODE:
+ gst_rtsp_media_factory_set_suspend_mode (factory,
+ g_value_get_enum (value));
+ break;
case PROP_EOS_SHUTDOWN:
gst_rtsp_media_factory_set_eos_shutdown (factory,
g_value_get_boolean (value));
@@ -443,6 +460,54 @@ gst_rtsp_media_factory_get_launch (GstRTSPMediaFactory * factory)
}
/**
+ * gst_rtsp_media_factory_set_suspend_mode:
+ * @factory: a #GstRTSPMediaFactory
+ * @mode: the new #GstRTSPSuspendMode
+ *
+ * Configure how media created from this factory will be suspended.
+ */
+void
+gst_rtsp_media_factory_set_suspend_mode (GstRTSPMediaFactory * factory,
+ GstRTSPSuspendMode mode)
+{
+ GstRTSPMediaFactoryPrivate *priv;
+
+ g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
+
+ priv = factory->priv;
+
+ GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+ priv->suspend_mode = mode;
+ GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+}
+
+/**
+ * gst_rtsp_media_factory_get_suspend_mode:
+ * @factory: a #GstRTSPMediaFactory
+ *
+ * Get how media created from this factory will be suspended.
+ *
+ * Returns: a #GstRTSPSuspendMode.
+ */
+GstRTSPSuspendMode
+gst_rtsp_media_factory_get_suspend_mode (GstRTSPMediaFactory * factory)
+{
+ GstRTSPMediaFactoryPrivate *priv;
+ GstRTSPSuspendMode result;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory),
+ GST_RTSP_SUSPEND_MODE_NONE);
+
+ priv = factory->priv;
+
+ GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+ result = priv->suspend_mode;
+ GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+
+ return result;
+}
+
+/**
* gst_rtsp_media_factory_set_shared:
* @factory: a #GstRTSPMediaFactory
* @shared: the new value
@@ -946,18 +1011,21 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
GstRTSPMediaFactoryPrivate *priv = factory->priv;
gboolean shared, eos_shutdown;
guint size;
+ GstRTSPSuspendMode suspend_mode;
GstRTSPLowerTrans protocols;
GstRTSPAddressPool *pool;
GstRTSPPermissions *perms;
/* configure the sharedness */
GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+ suspend_mode = priv->suspend_mode;
shared = priv->shared;
eos_shutdown = priv->eos_shutdown;
size = priv->buffer_size;
protocols = priv->protocols;
GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+ gst_rtsp_media_set_suspend_mode (media, suspend_mode);
gst_rtsp_media_set_shared (media, shared);
gst_rtsp_media_set_eos_shutdown (media, eos_shutdown);
gst_rtsp_media_set_buffer_size (media, size);
diff --git a/gst/rtsp-server/rtsp-media-factory.h b/gst/rtsp-server/rtsp-media-factory.h
index d8819b1..63f4d82 100644
--- a/gst/rtsp-server/rtsp-media-factory.h
+++ b/gst/rtsp-server/rtsp-media-factory.h
@@ -114,6 +114,10 @@ void gst_rtsp_media_factory_set_shared (GstRTSPMediaFacto
gboolean shared);
gboolean gst_rtsp_media_factory_is_shared (GstRTSPMediaFactory *factory);
+void gst_rtsp_media_factory_set_suspend_mode (GstRTSPMediaFactory *factory,
+ GstRTSPSuspendMode mode);
+GstRTSPSuspendMode gst_rtsp_media_factory_get_suspend_mode (GstRTSPMediaFactory *factory);
+
void gst_rtsp_media_factory_set_eos_shutdown (GstRTSPMediaFactory *factory,
gboolean eos_shutdown);
gboolean gst_rtsp_media_factory_is_eos_shutdown (GstRTSPMediaFactory *factory);
diff --git a/gst/rtsp-server/rtsp-media.c b/gst/rtsp-server/rtsp-media.c
index 5529845..2c72d97 100644
--- a/gst/rtsp-server/rtsp-media.c
+++ b/gst/rtsp-server/rtsp-media.c
@@ -80,12 +80,14 @@ struct _GstRTSPMediaPrivate
/* protected by lock */
GstRTSPPermissions *permissions;
gboolean shared;
+ gboolean suspend_mode;
gboolean reusable;
GstRTSPLowerTrans protocols;
gboolean reused;
gboolean eos_shutdown;
guint buffer_size;
GstRTSPAddressPool *pool;
+ gboolean blocked;
GstElement *element;
GRecMutex state_lock; /* locking order: state lock, lock */
@@ -121,6 +123,7 @@ struct _GstRTSPMediaPrivate
};
#define DEFAULT_SHARED FALSE
+#define DEFAULT_SUSPEND_MODE GST_RTSP_SUSPEND_MODE_NONE
#define DEFAULT_REUSABLE FALSE
#define DEFAULT_PROTOCOLS GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST | \
GST_RTSP_LOWER_TRANS_TCP
@@ -135,6 +138,7 @@ enum
{
PROP_0,
PROP_SHARED,
+ PROP_SUSPEND_MODE,
PROP_REUSABLE,
PROP_PROTOCOLS,
PROP_EOS_SHUTDOWN,
@@ -173,8 +177,33 @@ static gboolean default_query_position (GstRTSPMedia * media,
gint64 * position);
static gboolean default_query_stop (GstRTSPMedia * media, gint64 * stop);
+static gboolean wait_preroll (GstRTSPMedia * media);
+
static guint gst_rtsp_media_signals[SIGNAL_LAST] = { 0 };
+#define C_ENUM(v) ((gint) v)
+
+#define GST_TYPE_RTSP_SUSPEND_MODE (gst_rtsp_suspend_mode_get_type())
+GType
+gst_rtsp_suspend_mode_get_type (void)
+{
+ static gsize id = 0;
+ static const GEnumValue values[] = {
+ {C_ENUM (GST_RTSP_SUSPEND_MODE_NONE), "GST_RTSP_SUSPEND_MODE_NONE", "none"},
+ {C_ENUM (GST_RTSP_SUSPEND_MODE_PAUSE), "GST_RTSP_SUSPEND_MODE_PAUSE",
+ "pause"},
+ {C_ENUM (GST_RTSP_SUSPEND_MODE_RESET), "GST_RTSP_SUSPEND_MODE_RESET",
+ "reset"},
+ {0, NULL, NULL}
+ };
+
+ if (g_once_init_enter (&id)) {
+ GType tmp = g_enum_register_static ("GstRTSPSuspendMode", values);
+ g_once_init_leave (&id, tmp);
+ }
+ return (GType) id;
+}
+
G_DEFINE_TYPE (GstRTSPMedia, gst_rtsp_media, G_TYPE_OBJECT);
static void
@@ -195,6 +224,11 @@ gst_rtsp_media_class_init (GstRTSPMediaClass * klass)
"If this media pipeline can be shared", DEFAULT_SHARED,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_SUSPEND_MODE,
+ g_param_spec_enum ("suspend-mode", "Suspend Mode",
+ "How to suspend the media in PAUSED", GST_TYPE_RTSP_SUSPEND_MODE,
+ DEFAULT_SUSPEND_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
g_object_class_install_property (gobject_class, PROP_REUSABLE,
g_param_spec_boolean ("reusable", "Reusable",
"If this media pipeline can be reused after an unprepare",
@@ -273,6 +307,7 @@ gst_rtsp_media_init (GstRTSPMedia * media)
g_rec_mutex_init (&priv->state_lock);
priv->shared = DEFAULT_SHARED;
+ priv->suspend_mode = DEFAULT_SUSPEND_MODE;
priv->reusable = DEFAULT_REUSABLE;
priv->protocols = DEFAULT_PROTOCOLS;
priv->eos_shutdown = DEFAULT_EOS_SHUTDOWN;
@@ -325,6 +360,9 @@ gst_rtsp_media_get_property (GObject * object, guint propid,
case PROP_SHARED:
g_value_set_boolean (value, gst_rtsp_media_is_shared (media));
break;
+ case PROP_SUSPEND_MODE:
+ g_value_set_enum (value, gst_rtsp_media_get_suspend_mode (media));
+ break;
case PROP_REUSABLE:
g_value_set_boolean (value, gst_rtsp_media_is_reusable (media));
break;
@@ -359,6 +397,9 @@ gst_rtsp_media_set_property (GObject * object, guint propid,
case PROP_SHARED:
gst_rtsp_media_set_shared (media, g_value_get_boolean (value));
break;
+ case PROP_SUSPEND_MODE:
+ gst_rtsp_media_set_suspend_mode (media, g_value_get_enum (value));
+ break;
case PROP_REUSABLE:
gst_rtsp_media_set_reusable (media, g_value_get_boolean (value));
break;
@@ -604,6 +645,66 @@ gst_rtsp_media_get_permissions (GstRTSPMedia * media)
}
/**
+ * gst_rtsp_media_set_suspend_mode:
+ * @media: a #GstRTSPMedia
+ * @mode: the new #GstRTSPSuspendMode
+ *
+ * Control how @ media will be suspended after the SDP has been generated and
+ * after a PAUSE request has been performed.
+ *
+ * Media must be unprepared when setting the suspend mode.
+ */
+void
+gst_rtsp_media_set_suspend_mode (GstRTSPMedia * media, GstRTSPSuspendMode mode)
+{
+ GstRTSPMediaPrivate *priv;
+
+ g_return_if_fail (GST_IS_RTSP_MEDIA (media));
+
+ priv = media->priv;
+
+ g_rec_mutex_lock (&priv->state_lock);
+ if (priv->status == GST_RTSP_MEDIA_STATUS_PREPARED)
+ goto was_prepared;
+ priv->suspend_mode = mode;
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ return;
+
+ /* ERRORS */
+was_prepared:
+ {
+ GST_WARNING ("media %p was prepared", media);
+ g_rec_mutex_unlock (&priv->state_lock);
+ }
+}
+
+/**
+ * gst_rtsp_media_get_suspend_mode:
+ * @media: a #GstRTSPMedia
+ *
+ * Get how @media will be suspended.
+ *
+ * Returns: #GstRTSPSuspendMode.
+ */
+GstRTSPSuspendMode
+gst_rtsp_media_get_suspend_mode (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv;
+ GstRTSPSuspendMode res;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_RTSP_SUSPEND_MODE_NONE);
+
+ priv = media->priv;
+
+ g_rec_mutex_lock (&priv->state_lock);
+ res = priv->suspend_mode;
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ return res;
+}
+
+/**
* gst_rtsp_media_set_shared:
* @media: a #GstRTSPMedia
* @shared: the new value
@@ -1263,6 +1364,22 @@ conversion_failed:
}
}
+static void
+stream_update_blocked (GstRTSPStream * stream, GstRTSPMedia * media)
+{
+ gst_rtsp_stream_set_blocked (stream, media->priv->blocked);
+}
+
+static void
+media_streams_set_blocked (GstRTSPMedia * media, gboolean blocked)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+
+ GST_DEBUG ("media %p set blocked %d", media, blocked);
+ priv->blocked = blocked;
+ g_ptr_array_foreach (priv->streams, (GFunc) stream_update_blocked, media);
+}
+
/**
* gst_rtsp_media_seek:
* @media: a #GstRTSPMedia
@@ -1340,15 +1457,23 @@ gst_rtsp_media_seek (GstRTSPMedia * media, GstRTSPTimeRange * range)
GST_INFO ("seeking to %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
+ priv->status = GST_RTSP_MEDIA_STATUS_PREPARING;
+ if (priv->blocked)
+ media_streams_set_blocked (media, TRUE);
+
res = gst_element_seek (priv->pipeline, 1.0, GST_FORMAT_TIME,
flags, start_type, start, stop_type, stop);
/* and block for the seek to complete */
GST_INFO ("done seeking %d", res);
- gst_element_get_state (priv->pipeline, NULL, NULL, -1);
- GST_INFO ("prerolled again");
+ g_rec_mutex_unlock (&priv->state_lock);
- collect_media_stats (media);
+ /* wait until pipeline is prerolled again, this will also collect stats */
+ if (!wait_preroll (media))
+ goto preroll_failed;
+
+ g_rec_mutex_lock (&priv->state_lock);
+ GST_INFO ("prerolled again");
} else {
GST_INFO ("no seek needed");
res = TRUE;
@@ -1376,6 +1501,11 @@ not_supported:
GST_WARNING ("conversion to npt not supported");
return FALSE;
}
+preroll_failed:
+ {
+ GST_WARNING ("failed to preroll after seek");
+ return FALSE;
+ }
}
static void
@@ -1424,6 +1554,23 @@ gst_rtsp_media_get_status (GstRTSPMedia * media)
return result;
}
+static void
+stream_collect_blocking (GstRTSPStream * stream, gboolean * blocked)
+{
+ *blocked &= gst_rtsp_stream_is_blocking (stream);
+}
+
+static gboolean
+media_streams_blocking (GstRTSPMedia * media)
+{
+ gboolean blocking = TRUE;
+
+ g_ptr_array_foreach (media->priv->streams, (GFunc) stream_collect_blocking,
+ &blocking);
+
+ return blocking;
+}
+
/* called with state-lock */
static gboolean
default_handle_message (GstRTSPMedia * media, GstMessage * message)
@@ -1501,7 +1648,22 @@ default_handle_message (GstRTSPMedia * media, GstMessage * message)
break;
}
case GST_MESSAGE_ELEMENT:
+ {
+ const GstStructure *s;
+
+ s = gst_message_get_structure (message);
+ if (gst_structure_has_name (s, "GstRTSPStreamBlocking")) {
+ GST_DEBUG ("media received blocking message");
+ if (priv->blocked && media_streams_blocking (media)) {
+ GST_DEBUG ("media is blocking");
+ collect_media_stats (media);
+
+ if (priv->status == GST_RTSP_MEDIA_STATUS_PREPARING)
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARED);
+ }
+ }
break;
+ }
case GST_MESSAGE_STREAM_STATUS:
break;
case GST_MESSAGE_ASYNC_DONE:
@@ -1684,10 +1846,76 @@ struct _DynPaySignalHandlers
};
static gboolean
-start_prepare (GstRTSPMedia * media)
+start_preroll (GstRTSPMedia * media)
{
GstRTSPMediaPrivate *priv = media->priv;
GstStateChangeReturn ret;
+
+ GST_INFO ("setting pipeline to PAUSED for media %p", media);
+ /* first go to PAUSED */
+ ret = gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
+ priv->target_state = GST_STATE_PAUSED;
+
+ switch (ret) {
+ case GST_STATE_CHANGE_SUCCESS:
+ GST_INFO ("SUCCESS state change for media %p", media);
+ priv->seekable = TRUE;
+ break;
+ case GST_STATE_CHANGE_ASYNC:
+ GST_INFO ("ASYNC state change for media %p", media);
+ priv->seekable = TRUE;
+ break;
+ case GST_STATE_CHANGE_NO_PREROLL:
+ /* we need to go to PLAYING */
+ GST_INFO ("NO_PREROLL state change: live media %p", media);
+ /* FIXME we disable seeking for live streams for now. We should perform a
+ * seeking query in preroll instead */
+ priv->seekable = FALSE;
+ priv->is_live = TRUE;
+ /* start blocked to make sure nothing goes to the sink */
+ media_streams_set_blocked (media, TRUE);
+ ret = gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto state_failed;
+ break;
+ case GST_STATE_CHANGE_FAILURE:
+ goto state_failed;
+ }
+
+ return TRUE;
+
+state_failed:
+ {
+ GST_WARNING ("failed to preroll pipeline");
+ return FALSE;
+ }
+}
+
+static gboolean
+wait_preroll (GstRTSPMedia * media)
+{
+ GstRTSPMediaStatus status;
+
+ GST_DEBUG ("wait to preroll pipeline");
+
+ /* wait until pipeline is prerolled */
+ status = gst_rtsp_media_get_status (media);
+ if (status == GST_RTSP_MEDIA_STATUS_ERROR)
+ goto preroll_failed;
+
+ return TRUE;
+
+preroll_failed:
+ {
+ GST_WARNING ("failed to preroll pipeline");
+ return FALSE;
+ }
+}
+
+static gboolean
+start_prepare (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
guint i;
GList *walk;
@@ -1723,38 +1951,12 @@ start_prepare (GstRTSPMedia * media)
gst_bin_add (GST_BIN (priv->pipeline), priv->fakesink);
}
- GST_INFO ("setting pipeline to PAUSED for media %p", media);
- /* first go to PAUSED */
- ret = gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
- priv->target_state = GST_STATE_PAUSED;
-
- switch (ret) {
- case GST_STATE_CHANGE_SUCCESS:
- GST_INFO ("SUCCESS state change for media %p", media);
- priv->seekable = TRUE;
- break;
- case GST_STATE_CHANGE_ASYNC:
- GST_INFO ("ASYNC state change for media %p", media);
- priv->seekable = TRUE;
- break;
- case GST_STATE_CHANGE_NO_PREROLL:
- /* we need to go to PLAYING */
- GST_INFO ("NO_PREROLL state change: live media %p", media);
- /* FIXME we disable seeking for live streams for now. We should perform a
- * seeking query in preroll instead */
- priv->seekable = FALSE;
- priv->is_live = TRUE;
- ret = gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
- if (ret == GST_STATE_CHANGE_FAILURE)
- goto state_failed;
- break;
- case GST_STATE_CHANGE_FAILURE:
- goto state_failed;
- }
+ if (!start_preroll (media))
+ goto preroll_failed;
return FALSE;
-state_failed:
+preroll_failed:
{
GST_WARNING ("failed to preroll pipeline");
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_ERROR);
@@ -1780,7 +1982,6 @@ gboolean
gst_rtsp_media_prepare (GstRTSPMedia * media, GstRTSPThread * thread)
{
GstRTSPMediaPrivate *priv;
- GstRTSPMediaStatus status;
GstBus *bus;
GSource *source;
@@ -1856,9 +2057,8 @@ wait_status:
/* now wait for all pads to be prerolled, FIXME, we should somehow be
* able to do this async so that we don't block the server thread. */
- status = gst_rtsp_media_get_status (media);
- if (status == GST_RTSP_MEDIA_STATUS_ERROR)
- goto state_failed;
+ if (!wait_preroll (media))
+ goto preroll_failed;
g_signal_emit (media, gst_rtsp_media_signals[SIGNAL_PREPARED], 0, NULL);
@@ -1896,7 +2096,7 @@ no_rtpbin:
g_warning ("failed to create element 'rtpbin', check your installation");
return FALSE;
}
-state_failed:
+preroll_failed:
{
GST_WARNING ("failed to preroll pipeline");
gst_rtsp_media_unprepare (media);
@@ -2169,6 +2369,144 @@ gst_rtsp_media_get_time_provider (GstRTSPMedia * media, const gchar * address,
return provider;
}
+/**
+ * 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_undo_reset()
+ *
+ * @media must be prepared with gst_rtsp_media_prepare();
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+gst_rtsp_media_suspend (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+ GstStateChangeReturn ret;
+
+ 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;
+
+ 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);
+ priv->target_state = GST_STATE_PAUSED;
+ ret = gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto state_failed;
+ break;
+ case GST_RTSP_SUSPEND_MODE_RESET:
+ GST_DEBUG ("media %p suspend to NULL", media);
+ priv->target_state = GST_STATE_NULL;
+ ret = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto state_failed;
+ break;
+ default:
+ break;
+ }
+ /* let the streams do the state changes freely, if any */
+ media_streams_set_blocked (media, FALSE);
+ priv->status = 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;
+ }
+state_failed:
+ {
+ g_rec_mutex_unlock (&priv->state_lock);
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_ERROR);
+ GST_WARNING ("failed changing pipeline's state for media %p", media);
+ 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;
+
+ 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;
+
+ switch (priv->suspend_mode) {
+ case GST_RTSP_SUSPEND_MODE_NONE:
+ priv->status = GST_RTSP_MEDIA_STATUS_PREPARED;
+ break;
+ case GST_RTSP_SUSPEND_MODE_PAUSE:
+ priv->status = GST_RTSP_MEDIA_STATUS_PREPARED;
+ break;
+ case GST_RTSP_SUSPEND_MODE_RESET:
+ {
+ priv->status = 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;
+ }
+done:
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ return TRUE;
+
+ /* ERRORS */
+start_failed:
+ {
+ g_rec_mutex_unlock (&priv->state_lock);
+ GST_WARNING ("failed to preroll pipeline");
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_ERROR);
+ return FALSE;
+ }
+preroll_failed:
+ {
+ GST_WARNING ("failed to preroll pipeline");
+ return FALSE;
+ }
+}
+
+/* must be called with state-lock */
static void
media_set_pipeline_state_locked (GstRTSPMedia * media, GstState state)
{
@@ -2184,7 +2522,15 @@ media_set_pipeline_state_locked (GstRTSPMedia * media, GstState state)
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);
+
gst_element_set_state (priv->pipeline, state);
+
+ /* and suspend after pause */
+ if (state == GST_STATE_PAUSED)
+ gst_rtsp_media_suspend (media);
}
}
}
diff --git a/gst/rtsp-server/rtsp-media.h b/gst/rtsp-server/rtsp-media.h
index 016526d..735576b 100644
--- a/gst/rtsp-server/rtsp-media.h
+++ b/gst/rtsp-server/rtsp-media.h
@@ -41,11 +41,6 @@ typedef struct _GstRTSPMedia GstRTSPMedia;
typedef struct _GstRTSPMediaClass GstRTSPMediaClass;
typedef struct _GstRTSPMediaPrivate GstRTSPMediaPrivate;
-#include "rtsp-stream.h"
-#include "rtsp-thread-pool.h"
-#include "rtsp-permissions.h"
-#include "rtsp-address-pool.h"
-
/**
* GstRTSPMediaStatus:
* @GST_RTSP_MEDIA_STATUS_UNPREPARED: media pipeline not prerolled
@@ -53,6 +48,7 @@ typedef struct _GstRTSPMediaPrivate GstRTSPMediaPrivate;
* shutdown.
* @GST_RTSP_MEDIA_STATUS_PREPARING: media pipeline is prerolling
* @GST_RTSP_MEDIA_STATUS_PREPARED: media pipeline is prerolled
+ * @GST_RTSP_MEDIA_STATUS_SUSPENDED: media is suspended
* @GST_RTSP_MEDIA_STATUS_ERROR: media pipeline is in error
*
* The state of the media pipeline.
@@ -62,10 +58,34 @@ typedef enum {
GST_RTSP_MEDIA_STATUS_UNPREPARING = 1,
GST_RTSP_MEDIA_STATUS_PREPARING = 2,
GST_RTSP_MEDIA_STATUS_PREPARED = 3,
- GST_RTSP_MEDIA_STATUS_ERROR = 4
+ GST_RTSP_MEDIA_STATUS_SUSPENDED = 4,
+ GST_RTSP_MEDIA_STATUS_ERROR = 5
} GstRTSPMediaStatus;
/**
+ * GstRTSPSuspendMode:
+ * @GST_RTSP_SUSPEND_MODE_NONE: Media is not suspended
+ * @GST_RTSP_SUSPEND_MODE_PAUSE: Media is PAUSED in suspend
+ * @GST_RTSP_SUSPEND_MODE_RESET: The media is set to NULL when suspended
+ *
+ * The suspend mode of the media pipeline. A media pipeline is suspended right
+ * after creating the SDP and when the client preforms a PAUSED request.
+ */
+typedef enum {
+ GST_RTSP_SUSPEND_MODE_NONE = 0,
+ GST_RTSP_SUSPEND_MODE_PAUSE = 1,
+ GST_RTSP_SUSPEND_MODE_RESET = 2
+} GstRTSPSuspendMode;
+
+#define GST_TYPE_RTSP_SUSPEND_MODE (gst_rtsp_suspend_mode_get_type())
+GType gst_rtsp_suspend_mode_get_type (void);
+
+#include "rtsp-stream.h"
+#include "rtsp-thread-pool.h"
+#include "rtsp-permissions.h"
+#include "rtsp-address-pool.h"
+
+/**
* GstRTSPMedia:
*
* A class that contains the GStreamer element along with a list of
@@ -154,6 +174,12 @@ GstNetTimeProvider * gst_rtsp_media_get_time_provider (GstRTSPMedia *media,
gboolean gst_rtsp_media_prepare (GstRTSPMedia *media, GstRTSPThread *thread);
gboolean gst_rtsp_media_unprepare (GstRTSPMedia *media);
+void gst_rtsp_media_set_suspend_mode (GstRTSPMedia *media, GstRTSPSuspendMode mode);
+GstRTSPSuspendMode gst_rtsp_media_get_suspend_mode (GstRTSPMedia *media);
+
+gboolean gst_rtsp_media_suspend (GstRTSPMedia *media);
+gboolean gst_rtsp_media_unsuspend (GstRTSPMedia *media);
+
/* creating streams */
void gst_rtsp_media_collect_streams (GstRTSPMedia *media);
GstRTSPStream * gst_rtsp_media_create_stream (GstRTSPMedia *media,
diff --git a/gst/rtsp-server/rtsp-session-media.c b/gst/rtsp-server/rtsp-session-media.c
index 796da5a..d042ea0 100644
--- a/gst/rtsp-server/rtsp-session-media.c
+++ b/gst/rtsp-server/rtsp-session-media.c
@@ -138,11 +138,13 @@ gst_rtsp_session_media_new (const gchar * path, GstRTSPMedia * media)
GstRTSPSessionMediaPrivate *priv;
GstRTSPSessionMedia *result;
guint n_streams;
+ GstRTSPMediaStatus status;
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
- g_return_val_if_fail (gst_rtsp_media_get_status (media) ==
- GST_RTSP_MEDIA_STATUS_PREPARED, NULL);
+ status = gst_rtsp_media_get_status (media);
+ g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
+ GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
result = g_object_new (GST_TYPE_RTSP_SESSION_MEDIA, NULL);
priv = result->priv;
diff --git a/gst/rtsp-server/rtsp-session.c b/gst/rtsp-server/rtsp-session.c
index 204ccf7..b2ff36d 100644
--- a/gst/rtsp-server/rtsp-session.c
+++ b/gst/rtsp-server/rtsp-session.c
@@ -205,12 +205,14 @@ gst_rtsp_session_manage_media (GstRTSPSession * sess, const gchar * path,
{
GstRTSPSessionPrivate *priv;
GstRTSPSessionMedia *result;
+ GstRTSPMediaStatus status;
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
- g_return_val_if_fail (gst_rtsp_media_get_status (media) ==
- GST_RTSP_MEDIA_STATUS_PREPARED, NULL);
+ status = gst_rtsp_media_get_status (media);
+ g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
+ GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
priv = sess->priv;
diff --git a/tests/check/gst/media.c b/tests/check/gst/media.c
index ca2c324..23e8586 100644
--- a/tests/check/gst/media.c
+++ b/tests/check/gst/media.c
@@ -300,6 +300,50 @@ GST_START_TEST (test_media_take_pipeline)
GST_END_TEST;
+GST_START_TEST (test_media_reset)
+{
+ GstRTSPMediaFactory *factory;
+ GstRTSPMedia *media;
+ GstRTSPUrl *url;
+ GstRTSPThreadPool *pool;
+ GstRTSPThread *thread;
+
+ pool = gst_rtsp_thread_pool_new ();
+
+ factory = gst_rtsp_media_factory_new ();
+ fail_if (gst_rtsp_media_factory_is_shared (factory));
+ gst_rtsp_url_parse ("rtsp://localhost:8554/test", &url);
+
+ gst_rtsp_media_factory_set_launch (factory,
+ "( videotestsrc ! rtpvrawpay pt=96 name=pay0 )");
+
+ media = gst_rtsp_media_factory_construct (factory, url);
+ fail_unless (GST_IS_RTSP_MEDIA (media));
+
+ thread = gst_rtsp_thread_pool_get_thread (pool,
+ GST_RTSP_THREAD_TYPE_MEDIA, NULL);
+ fail_unless (gst_rtsp_media_prepare (media, thread));
+ fail_unless (gst_rtsp_media_suspend (media));
+ fail_unless (gst_rtsp_media_unprepare (media));
+ g_object_unref (media);
+
+ media = gst_rtsp_media_factory_construct (factory, url);
+ fail_unless (GST_IS_RTSP_MEDIA (media));
+
+ thread = gst_rtsp_thread_pool_get_thread (pool,
+ GST_RTSP_THREAD_TYPE_MEDIA, NULL);
+ gst_rtsp_media_set_suspend_mode (media, GST_RTSP_SUSPEND_MODE_RESET);
+ fail_unless (gst_rtsp_media_prepare (media, thread));
+ fail_unless (gst_rtsp_media_suspend (media));
+ fail_unless (gst_rtsp_media_unprepare (media));
+ g_object_unref (media);
+
+ gst_rtsp_url_free (url);
+ g_object_unref (factory);
+}
+
+GST_END_TEST;
+
static Suite *
rtspmedia_suite (void)
{
@@ -313,6 +357,7 @@ rtspmedia_suite (void)
tcase_add_test (tc, test_media_prepare);
tcase_add_test (tc, test_media_dyn_prepare);
tcase_add_test (tc, test_media_take_pipeline);
+ tcase_add_test (tc, test_media_reset);
return s;
}
diff --git a/tests/check/gst/mediafactory.c b/tests/check/gst/mediafactory.c
index 73e4883..b6b250b 100644
--- a/tests/check/gst/mediafactory.c
+++ b/tests/check/gst/mediafactory.c
@@ -280,6 +280,40 @@ GST_START_TEST (test_permissions)
GST_END_TEST;
+GST_START_TEST (test_reset)
+{
+ GstRTSPMediaFactory *factory;
+ GstRTSPMedia *media;
+ GstRTSPUrl *url;
+
+ factory = gst_rtsp_media_factory_new ();
+ fail_if (gst_rtsp_media_factory_is_shared (factory));
+ gst_rtsp_url_parse ("rtsp://localhost:8554/test", &url);
+
+ gst_rtsp_media_factory_set_launch (factory,
+ "( videotestsrc ! rtpvrawpay pt=96 name=pay0 )");
+
+ media = gst_rtsp_media_factory_construct (factory, url);
+ fail_unless (GST_IS_RTSP_MEDIA (media));
+ fail_if (gst_rtsp_media_get_suspend_mode (media) !=
+ GST_RTSP_SUSPEND_MODE_NONE);
+ g_object_unref (media);
+
+ gst_rtsp_media_factory_set_suspend_mode (factory,
+ GST_RTSP_SUSPEND_MODE_RESET);
+
+ media = gst_rtsp_media_factory_construct (factory, url);
+ fail_unless (GST_IS_RTSP_MEDIA (media));
+ fail_if (gst_rtsp_media_get_suspend_mode (media) !=
+ GST_RTSP_SUSPEND_MODE_RESET);
+ g_object_unref (media);
+
+ gst_rtsp_url_free (url);
+ g_object_unref (factory);
+}
+
+GST_END_TEST;
+
static Suite *
rtspmediafactory_suite (void)
{
@@ -294,6 +328,7 @@ rtspmediafactory_suite (void)
tcase_add_test (tc, test_shared);
tcase_add_test (tc, test_addresspool);
tcase_add_test (tc, test_permissions);
+ tcase_add_test (tc, test_reset);
return s;
}