rtsp-client: Lock shared media

For shared media we got race conditions. Concurrently rtsp clients might
suspend or unsuspend the shared media and thus change the state without
the clients expecting that.
By introducing a lock that can be taken by callers such as rtsp_client
one can force rtsp clients calling, eg. PLAY, SETUP and that uses shared media,
to handle the media sequentially thus allowing one client to finish its
rtsp call before another client calls on the same media.

https://gitlab.freedesktop.org/gstreamer/gst-rtsp-server/issues/86
Fixes #86
This commit is contained in:
Kristofer 2019-10-16 13:20:54 +00:00 committed by Sebastian Dröge
parent 74b19b3709
commit 1a01d20e40
3 changed files with 154 additions and 8 deletions

View file

@ -1356,6 +1356,7 @@ handle_teardown_request (GstRTSPClient * client, GstRTSPContext * ctx)
GstRTSPClientClass *klass; GstRTSPClientClass *klass;
GstRTSPSession *session; GstRTSPSession *session;
GstRTSPSessionMedia *sessmedia; GstRTSPSessionMedia *sessmedia;
GstRTSPMedia *media;
GstRTSPStatusCode code; GstRTSPStatusCode code;
gchar *path; gchar *path;
gint matched; gint matched;
@ -1386,6 +1387,10 @@ handle_teardown_request (GstRTSPClient * client, GstRTSPContext * ctx)
ctx->sessmedia = sessmedia; ctx->sessmedia = sessmedia;
media = gst_rtsp_session_media_get_media (sessmedia);
g_object_ref (media);
gst_rtsp_media_lock (media);
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_TEARDOWN_REQUEST], g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_TEARDOWN_REQUEST],
0, ctx, &sig_result); 0, ctx, &sig_result);
if (sig_result != GST_RTSP_STS_OK) { if (sig_result != GST_RTSP_STS_OK) {
@ -1414,6 +1419,9 @@ handle_teardown_request (GstRTSPClient * client, GstRTSPContext * ctx)
gst_rtsp_session_pool_remove (priv->session_pool, session); gst_rtsp_session_pool_remove (priv->session_pool, session);
} }
gst_rtsp_media_unlock (media);
g_object_unref (media);
return TRUE; return TRUE;
/* ERRORS */ /* ERRORS */
@ -1449,6 +1457,8 @@ sig_failed:
GST_ERROR ("client %p: pre signal returned error: %s", client, GST_ERROR ("client %p: pre signal returned error: %s", client,
gst_rtsp_status_as_text (sig_result)); gst_rtsp_status_as_text (sig_result));
send_generic_response (client, sig_result, ctx); send_generic_response (client, sig_result, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
} }
@ -1617,6 +1627,8 @@ handle_pause_request (GstRTSPClient * client, GstRTSPContext * ctx)
g_free (path); g_free (path);
media = gst_rtsp_session_media_get_media (sessmedia); media = gst_rtsp_session_media_get_media (sessmedia);
g_object_ref (media);
gst_rtsp_media_lock (media);
n = gst_rtsp_media_n_streams (media); n = gst_rtsp_media_n_streams (media);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
GstRTSPStream *stream = gst_rtsp_media_get_stream (media, i); GstRTSPStream *stream = gst_rtsp_media_get_stream (media, i);
@ -1655,6 +1667,9 @@ handle_pause_request (GstRTSPClient * client, GstRTSPContext * ctx)
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST], 0, ctx); g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST], 0, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return TRUE; return TRUE;
/* ERRORS */ /* ERRORS */
@ -1690,6 +1705,8 @@ sig_failed:
GST_ERROR ("client %p: pre signal returned error: %s", client, GST_ERROR ("client %p: pre signal returned error: %s", client,
gst_rtsp_status_as_text (sig_result)); gst_rtsp_status_as_text (sig_result));
send_generic_response (client, sig_result, ctx); send_generic_response (client, sig_result, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
invalid_state: invalid_state:
@ -1697,12 +1714,16 @@ invalid_state:
GST_ERROR ("client %p: not PLAYING or RECORDING", client); GST_ERROR ("client %p: not PLAYING or RECORDING", client);
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
ctx); ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
not_supported: not_supported:
{ {
GST_ERROR ("client %p: pausing not supported", client); GST_ERROR ("client %p: pausing not supported", client);
send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx); send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
} }
@ -1974,6 +1995,9 @@ handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
ctx->sessmedia = sessmedia; ctx->sessmedia = sessmedia;
ctx->media = media = gst_rtsp_session_media_get_media (sessmedia); ctx->media = media = gst_rtsp_session_media_get_media (sessmedia);
g_object_ref (media);
gst_rtsp_media_lock (media);
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_PLAY_REQUEST], 0, g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_PLAY_REQUEST], 0,
ctx, &sig_result); ctx, &sig_result);
if (sig_result != GST_RTSP_STS_OK) { if (sig_result != GST_RTSP_STS_OK) {
@ -2060,6 +2084,9 @@ handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST], 0, ctx); g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST], 0, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return TRUE; return TRUE;
/* ERRORS */ /* ERRORS */
@ -2094,6 +2121,8 @@ sig_failed:
GST_ERROR ("client %p: pre signal returned error: %s", client, GST_ERROR ("client %p: pre signal returned error: %s", client,
gst_rtsp_status_as_text (sig_result)); gst_rtsp_status_as_text (sig_result));
send_generic_response (client, sig_result, ctx); send_generic_response (client, sig_result, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
invalid_state: invalid_state:
@ -2101,6 +2130,8 @@ invalid_state:
GST_ERROR ("client %p: not PLAYING or READY", client); GST_ERROR ("client %p: not PLAYING or READY", client);
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
ctx); ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
pipeline_error: pipeline_error:
@ -2108,42 +2139,56 @@ pipeline_error:
GST_ERROR ("client %p: failed to configure the pipeline", client); GST_ERROR ("client %p: failed to configure the pipeline", client);
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
ctx); ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
unsuspend_failed: unsuspend_failed:
{ {
GST_ERROR ("client %p: unsuspend failed", client); GST_ERROR ("client %p: unsuspend failed", client);
send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx); send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
invalid_mode: invalid_mode:
{ {
GST_ERROR ("client %p: seek failed", client); GST_ERROR ("client %p: seek failed", client);
send_generic_response (client, code, ctx); send_generic_response (client, code, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
unsupported_mode: unsupported_mode:
{ {
GST_ERROR ("client %p: media does not support PLAY", client); GST_ERROR ("client %p: media does not support PLAY", client);
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx); send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
get_rates_error: get_rates_error:
{ {
GST_ERROR ("client %p: failed obtaining rate and applied_rate", client); GST_ERROR ("client %p: failed obtaining rate and applied_rate", client);
send_generic_response (client, GST_RTSP_STS_INTERNAL_SERVER_ERROR, ctx); send_generic_response (client, GST_RTSP_STS_INTERNAL_SERVER_ERROR, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
adjust_play_response_failed: adjust_play_response_failed:
{ {
GST_ERROR ("client %p: failed to adjust play response", client); GST_ERROR ("client %p: failed to adjust play response", client);
send_generic_response (client, code, ctx); send_generic_response (client, code, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
rtp_info_error: rtp_info_error:
{ {
GST_ERROR ("client %p: failed to add RTP-Info", client); GST_ERROR ("client %p: failed to add RTP-Info", client);
send_generic_response (client, GST_RTSP_STS_INTERNAL_SERVER_ERROR, ctx); send_generic_response (client, GST_RTSP_STS_INTERNAL_SERVER_ERROR, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
} }
@ -2648,13 +2693,17 @@ handle_setup_request (GstRTSPClient * client, GstRTSPContext * ctx)
/* get a handle to the configuration of the media in the session */ /* get a handle to the configuration of the media in the session */
media = find_media (client, ctx, path, &matched); media = find_media (client, ctx, path, &matched);
/* need to suspend the media, if the protocol has changed */ /* need to suspend the media, if the protocol has changed */
if (media != NULL) if (media != NULL) {
gst_rtsp_media_lock (media);
gst_rtsp_media_suspend (media); gst_rtsp_media_suspend (media);
}
} else { } else {
if ((media = gst_rtsp_session_media_get_media (sessmedia))) if ((media = gst_rtsp_session_media_get_media (sessmedia))) {
g_object_ref (media); g_object_ref (media);
else gst_rtsp_media_lock (media);
} else {
goto media_not_found; goto media_not_found;
}
} }
/* no media, not found then */ /* no media, not found then */
if (media == NULL) if (media == NULL)
@ -2870,12 +2919,14 @@ handle_setup_request (GstRTSPClient * client, GstRTSPContext * ctx)
gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY); gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
break; break;
} }
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST], 0, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
g_object_unref (session); g_object_unref (session);
g_free (path); g_free (path);
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST], 0, ctx);
return TRUE; return TRUE;
/* ERRORS */ /* ERRORS */
@ -2913,6 +2964,7 @@ control_not_found:
{ {
GST_ERROR ("client %p: no control in path '%s'", client, path); GST_ERROR ("client %p: no control in path '%s'", client, path);
send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx); send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
goto cleanup_session; goto cleanup_session;
} }
@ -2921,6 +2973,7 @@ stream_not_found:
GST_ERROR ("client %p: stream '%s' not found", client, GST_ERROR ("client %p: stream '%s' not found", client,
GST_STR_NULL (control)); GST_STR_NULL (control));
send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx); send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
goto cleanup_session; goto cleanup_session;
} }
@ -2929,6 +2982,7 @@ sig_failed:
GST_ERROR ("client %p: pre signal returned error: %s", client, GST_ERROR ("client %p: pre signal returned error: %s", client,
gst_rtsp_status_as_text (sig_result)); gst_rtsp_status_as_text (sig_result));
send_generic_response (client, sig_result, ctx); send_generic_response (client, sig_result, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
goto cleanup_path; goto cleanup_path;
} }
@ -2936,6 +2990,7 @@ service_unavailable:
{ {
GST_ERROR ("client %p: can't create session", client); GST_ERROR ("client %p: can't create session", client);
send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx); send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
goto cleanup_session; goto cleanup_session;
} }
@ -2948,6 +3003,7 @@ sessmedia_unavailable:
configure_media_failed_no_reply: configure_media_failed_no_reply:
{ {
GST_ERROR ("client %p: configure_media failed", client); GST_ERROR ("client %p: configure_media failed", client);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
/* error reply is already sent */ /* error reply is already sent */
goto cleanup_session; goto cleanup_session;
@ -2991,8 +3047,10 @@ keymgmt_error:
{ {
cleanup_transport: cleanup_transport:
gst_rtsp_transport_free (ct); gst_rtsp_transport_free (ct);
if (media) if (media) {
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
}
cleanup_session: cleanup_session:
if (new_session) if (new_session)
gst_rtsp_session_pool_remove (priv->session_pool, session); gst_rtsp_session_pool_remove (priv->session_pool, session);
@ -3105,6 +3163,8 @@ handle_describe_request (GstRTSPClient * client, GstRTSPContext * ctx)
if (!(media = find_media (client, ctx, path, NULL))) if (!(media = find_media (client, ctx, path, NULL)))
goto no_media; goto no_media;
gst_rtsp_media_lock (media);
if (!(gst_rtsp_media_get_transport_mode (media) & if (!(gst_rtsp_media_get_transport_mode (media) &
GST_RTSP_TRANSPORT_MODE_PLAY)) GST_RTSP_TRANSPORT_MODE_PLAY))
goto unsupported_mode; goto unsupported_mode;
@ -3115,7 +3175,6 @@ handle_describe_request (GstRTSPClient * client, GstRTSPContext * ctx)
/* we suspend after the describe */ /* we suspend after the describe */
gst_rtsp_media_suspend (media); gst_rtsp_media_suspend (media);
g_object_unref (media);
gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK, gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request); gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
@ -3140,6 +3199,9 @@ handle_describe_request (GstRTSPClient * client, GstRTSPContext * ctx)
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST], g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST],
0, ctx); 0, ctx);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return TRUE; return TRUE;
/* ERRORS */ /* ERRORS */
@ -3180,6 +3242,7 @@ unsupported_mode:
GST_ERROR ("client %p: media does not support DESCRIBE", client); GST_ERROR ("client %p: media does not support DESCRIBE", client);
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx); send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
g_free (path); g_free (path);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
return FALSE; return FALSE;
} }
@ -3188,6 +3251,7 @@ no_sdp:
GST_ERROR ("client %p: can't create SDP", client); GST_ERROR ("client %p: can't create SDP", client);
send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx); send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
g_free (path); g_free (path);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
return FALSE; return FALSE;
} }
@ -3284,6 +3348,7 @@ handle_announce_request (GstRTSPClient * client, GstRTSPContext * ctx)
goto no_media; goto no_media;
ctx->media = media; ctx->media = media;
gst_rtsp_media_lock (media);
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_ANNOUNCE_REQUEST], g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_ANNOUNCE_REQUEST],
0, ctx, &sig_result); 0, ctx, &sig_result);
@ -3319,7 +3384,6 @@ handle_announce_request (GstRTSPClient * client, GstRTSPContext * ctx)
/* we suspend after the announce */ /* we suspend after the announce */
gst_rtsp_media_suspend (media); gst_rtsp_media_suspend (media);
g_object_unref (media);
send_message (client, ctx, ctx->response, FALSE); send_message (client, ctx, ctx->response, FALSE);
@ -3328,6 +3392,9 @@ handle_announce_request (GstRTSPClient * client, GstRTSPContext * ctx)
gst_sdp_message_free (sdp); gst_sdp_message_free (sdp);
g_free (path); g_free (path);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return TRUE; return TRUE;
no_uri: no_uri:
@ -3382,6 +3449,8 @@ sig_failed:
gst_rtsp_status_as_text (sig_result)); gst_rtsp_status_as_text (sig_result));
send_generic_response (client, sig_result, ctx); send_generic_response (client, sig_result, ctx);
gst_sdp_message_free (sdp); gst_sdp_message_free (sdp);
gst_rtsp_media_unlock (media);
g_object_unref (media);
return FALSE; return FALSE;
} }
unsupported_mode: unsupported_mode:
@ -3389,6 +3458,7 @@ unsupported_mode:
GST_ERROR ("client %p: media does not support ANNOUNCE", client); GST_ERROR ("client %p: media does not support ANNOUNCE", client);
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx); send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
g_free (path); g_free (path);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
gst_sdp_message_free (sdp); gst_sdp_message_free (sdp);
return FALSE; return FALSE;
@ -3398,6 +3468,7 @@ unhandled_sdp:
GST_ERROR ("client %p: can't handle SDP", client); GST_ERROR ("client %p: can't handle SDP", client);
send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_MEDIA_TYPE, ctx); send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_MEDIA_TYPE, ctx);
g_free (path); g_free (path);
gst_rtsp_media_unlock (media);
g_object_unref (media); g_object_unref (media);
gst_sdp_message_free (sdp); gst_sdp_message_free (sdp);
return FALSE; return FALSE;

View file

@ -90,6 +90,12 @@ struct _GstRTSPMediaPrivate
GMutex lock; GMutex lock;
GCond cond; GCond cond;
/* the global lock is used to lock the entire media. This is needed by callers
such as rtsp_client to protect the media when it is shared by many clients.
The lock prevents that concurrenting clients messes up media.
Typically the lock is taken in external API calls such as SETUP */
GMutex global_lock;
/* protected by lock */ /* protected by lock */
GstRTSPPermissions *permissions; GstRTSPPermissions *permissions;
gboolean shared; gboolean shared;
@ -459,6 +465,7 @@ gst_rtsp_media_init (GstRTSPMedia * media)
priv->streams = g_ptr_array_new_with_free_func (g_object_unref); priv->streams = g_ptr_array_new_with_free_func (g_object_unref);
g_mutex_init (&priv->lock); g_mutex_init (&priv->lock);
g_mutex_init (&priv->global_lock);
g_cond_init (&priv->cond); g_cond_init (&priv->cond);
g_rec_mutex_init (&priv->state_lock); g_rec_mutex_init (&priv->state_lock);
@ -512,6 +519,7 @@ gst_rtsp_media_finalize (GObject * obj)
gst_object_unref (priv->clock); gst_object_unref (priv->clock);
g_free (priv->multicast_iface); g_free (priv->multicast_iface);
g_mutex_clear (&priv->lock); g_mutex_clear (&priv->lock);
g_mutex_clear (&priv->global_lock);
g_cond_clear (&priv->cond); g_cond_clear (&priv->cond);
g_rec_mutex_clear (&priv->state_lock); g_rec_mutex_clear (&priv->state_lock);
@ -3994,6 +4002,54 @@ get_clock_unlocked (GstRTSPMedia * media)
return gst_pipeline_get_clock (GST_PIPELINE_CAST (media->priv->pipeline)); return gst_pipeline_get_clock (GST_PIPELINE_CAST (media->priv->pipeline));
} }
/**
* gst_rtsp_media_lock:
* @media: a #GstRTSPMedia
*
* Lock the entire media. This is needed by callers such as rtsp_client to
* protect the media when it is shared by many clients.
* The lock prevents that concurrent clients alters the shared media,
* while one client already is working with it.
* Typically the lock is taken in external RTSP API calls that uses shared media
* such as DESCRIBE, SETUP, ANNOUNCE, TEARDOWN, PLAY, PAUSE.
*
* As best practice take the lock as soon as the function get hold of a shared
* media object. Release the lock right before the function returns.
*
* Since: 1.18
*/
void
gst_rtsp_media_lock (GstRTSPMedia * media)
{
GstRTSPMediaPrivate *priv;
g_return_if_fail (GST_IS_RTSP_MEDIA (media));
priv = media->priv;
g_mutex_lock (&priv->global_lock);
}
/**
* gst_rtsp_media_unlock:
* @media: a #GstRTSPMedia
*
* Unlock the media.
*
* Since: 1.18
*/
void
gst_rtsp_media_unlock (GstRTSPMedia * media)
{
GstRTSPMediaPrivate *priv;
g_return_if_fail (GST_IS_RTSP_MEDIA (media));
priv = media->priv;
g_mutex_unlock (&priv->global_lock);
}
/** /**
* gst_rtsp_media_get_clock: * gst_rtsp_media_get_clock:
* @media: a #GstRTSPMedia * @media: a #GstRTSPMedia
@ -4369,6 +4425,14 @@ gst_rtsp_media_suspend (GstRTSPMedia * media)
GST_FIXME ("suspend for dynamic pipelines needs fixing"); GST_FIXME ("suspend for dynamic pipelines needs fixing");
/* this typically can happen for shared media. */
if (priv->prepare_count > 1 &&
priv->status == GST_RTSP_MEDIA_STATUS_SUSPENDED) {
goto done;
} else if (priv->prepare_count > 1) {
goto prepared_by_other_client;
}
g_rec_mutex_lock (&priv->state_lock); g_rec_mutex_lock (&priv->state_lock);
if (priv->status != GST_RTSP_MEDIA_STATUS_PREPARED) if (priv->status != GST_RTSP_MEDIA_STATUS_PREPARED)
goto not_prepared; goto not_prepared;
@ -4390,6 +4454,11 @@ done:
return TRUE; return TRUE;
/* ERRORS */ /* ERRORS */
prepared_by_other_client:
{
GST_WARNING ("media %p was prepared by other client", media);
return FALSE;
}
not_prepared: not_prepared:
{ {
g_rec_mutex_unlock (&priv->state_lock); g_rec_mutex_unlock (&priv->state_lock);

View file

@ -364,6 +364,12 @@ GstRTSPStream * gst_rtsp_media_create_stream (GstRTSPMedia *media,
/* dealing with the media */ /* dealing with the media */
GST_RTSP_SERVER_API
void gst_rtsp_media_lock (GstRTSPMedia *media);
GST_RTSP_SERVER_API
void gst_rtsp_media_unlock (GstRTSPMedia *media);
GST_RTSP_SERVER_API GST_RTSP_SERVER_API
GstClock * gst_rtsp_media_get_clock (GstRTSPMedia *media); GstClock * gst_rtsp_media_get_clock (GstRTSPMedia *media);