diff options
author | Wim Taymans <wtaymans@redhat.com> | 2013-11-28 13:47:07 +0100 |
---|---|---|
committer | Wim Taymans <wtaymans@redhat.com> | 2013-11-28 13:47:07 +0100 |
commit | 0024b736dbbcc990b52c0520e78173589485a85d (patch) | |
tree | 3a6cb2afeac94e8fa515304ffc6760d833e114cb | |
parent | b63c8356ce368e7573f37d455bc8472e3702767a (diff) |
foowork3
-rw-r--r-- | examples/test-readme.c | 4 | ||||
-rw-r--r-- | gst/rtsp-server/rtsp-client.c | 13 | ||||
-rw-r--r-- | gst/rtsp-server/rtsp-media-factory.c | 68 | ||||
-rw-r--r-- | gst/rtsp-server/rtsp-media-factory.h | 4 | ||||
-rw-r--r-- | gst/rtsp-server/rtsp-media.c | 422 | ||||
-rw-r--r-- | gst/rtsp-server/rtsp-media.h | 38 | ||||
-rw-r--r-- | gst/rtsp-server/rtsp-session-media.c | 6 | ||||
-rw-r--r-- | gst/rtsp-server/rtsp-session.c | 6 | ||||
-rw-r--r-- | tests/check/gst/media.c | 45 | ||||
-rw-r--r-- | tests/check/gst/mediafactory.c | 35 |
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; } |