X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Frtsp-server%2Frtsp-media.c;h=6b81e254e5dd1bdcb465c02b636869dde684d2cb;hb=376488d8c7d0a92c56065070f9003e699533c3e5;hp=b827c7279bf458b7332467cb3011dcc45b3104fa;hpb=de2a70bb10b1e0111b3cbfa5c45c374061223965;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/rtsp-server/rtsp-media.c b/gst/rtsp-server/rtsp-media.c index b827c72..6b81e25 100644 --- a/gst/rtsp-server/rtsp-media.c +++ b/gst/rtsp-server/rtsp-media.c @@ -174,7 +174,10 @@ static void gst_rtsp_media_finalize (GObject * obj); static gboolean default_handle_message (GstRTSPMedia * media, GstMessage * message); static void finish_unprepare (GstRTSPMedia * media); +static gboolean default_prepare (GstRTSPMedia * media, GstRTSPThread * thread); static gboolean default_unprepare (GstRTSPMedia * media); +static gboolean default_suspend (GstRTSPMedia * media); +static gboolean default_unsuspend (GstRTSPMedia * media); static gboolean default_convert_range (GstRTSPMedia * media, GstRTSPTimeRange * range, GstRTSPRangeUnit unit); static gboolean default_query_position (GstRTSPMedia * media, @@ -285,27 +288,30 @@ gst_rtsp_media_class_init (GstRTSPMediaClass * klass) gst_rtsp_media_signals[SIGNAL_PREPARED] = g_signal_new ("prepared", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaClass, prepared), NULL, NULL, - g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); + g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE); gst_rtsp_media_signals[SIGNAL_UNPREPARED] = g_signal_new ("unprepared", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaClass, unprepared), NULL, NULL, - g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); + g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE); gst_rtsp_media_signals[SIGNAL_TARGET_STATE] = g_signal_new ("target-state", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaClass, new_state), NULL, - NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaClass, target_state), + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT); gst_rtsp_media_signals[SIGNAL_NEW_STATE] = g_signal_new ("new-state", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaClass, new_state), NULL, NULL, - g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT); GST_DEBUG_CATEGORY_INIT (rtsp_media_debug, "rtspmedia", 0, "GstRTSPMedia"); klass->handle_message = default_handle_message; + klass->prepare = default_prepare; klass->unprepare = default_unprepare; + klass->suspend = default_suspend; + klass->unsuspend = default_unsuspend; klass->convert_range = default_convert_range; klass->query_position = default_query_position; klass->query_stop = default_query_stop; @@ -446,28 +452,74 @@ gst_rtsp_media_set_property (GObject * object, guint propid, } } +typedef struct +{ + gint64 position; + gboolean ret; +} DoQueryPositionData; + +static void +do_query_position (GstRTSPStream * stream, DoQueryPositionData * data) +{ + gint64 tmp; + + if (gst_rtsp_stream_query_position (stream, &tmp)) { + data->position = MAX (data->position, tmp); + data->ret = TRUE; + } +} + static gboolean default_query_position (GstRTSPMedia * media, gint64 * position) { - return gst_element_query_position (media->priv->pipeline, GST_FORMAT_TIME, - position); + GstRTSPMediaPrivate *priv; + DoQueryPositionData data; + + priv = media->priv; + + data.position = -1; + data.ret = FALSE; + + g_ptr_array_foreach (priv->streams, (GFunc) do_query_position, &data); + + *position = data.position; + + return data.ret; +} + +typedef struct +{ + gint64 stop; + gboolean ret; +} DoQueryStopData; + +static void +do_query_stop (GstRTSPStream * stream, DoQueryStopData * data) +{ + gint64 tmp; + + if (gst_rtsp_stream_query_stop (stream, &tmp)) { + data->stop = MAX (data->stop, tmp); + data->ret = TRUE; + } } static gboolean default_query_stop (GstRTSPMedia * media, gint64 * stop) { - GstQuery *query; - gboolean res; + GstRTSPMediaPrivate *priv; + DoQueryStopData data; - query = gst_query_new_segment (GST_FORMAT_TIME); - if ((res = gst_element_query (media->priv->pipeline, query))) { - GstFormat format; - gst_query_parse_segment (query, NULL, &format, NULL, stop); - if (format != GST_FORMAT_TIME) - *stop = -1; - } - gst_query_unref (query); - return res; + priv = media->priv; + + data.stop = -1; + data.ret = FALSE; + + g_ptr_array_foreach (priv->streams, (GFunc) do_query_stop, &data); + + *stop = data.stop; + + return data.ret; } static GstElement * @@ -485,7 +537,7 @@ static void collect_media_stats (GstRTSPMedia * media) { GstRTSPMediaPrivate *priv = media->priv; - gint64 position, stop; + gint64 position = 0, stop = -1; if (priv->status != GST_RTSP_MEDIA_STATUS_PREPARED && priv->status != GST_RTSP_MEDIA_STATUS_PREPARING) @@ -1317,8 +1369,8 @@ gst_rtsp_media_n_streams (GstRTSPMedia * media) * * Retrieve the stream with index @idx from @media. * - * Returns: (transfer none): the #GstRTSPStream at index @idx or %NULL when a stream with - * that index did not exist. + * Returns: (nullable) (transfer none): the #GstRTSPStream at index + * @idx or %NULL when a stream with that index did not exist. */ GstRTSPStream * gst_rtsp_media_get_stream (GstRTSPMedia * media, guint idx) @@ -1347,8 +1399,9 @@ gst_rtsp_media_get_stream (GstRTSPMedia * media, guint idx) * * Find a stream in @media with @control as the control uri. * - * Returns: (transfer none): the #GstRTSPStream with control uri @control - * or %NULL when a stream with that control did not exist. + * Returns: (nullable) (transfer none): the #GstRTSPStream with + * control uri @control or %NULL when a stream with that control did + * not exist. */ GstRTSPStream * gst_rtsp_media_find_stream (GstRTSPMedia * media, const gchar * control) @@ -1621,7 +1674,7 @@ gst_rtsp_media_seek (GstRTSPMedia * media, GstRTSPTimeRange * range) flags |= GST_SEEK_FLAG_KEY_UNIT; } - /* FIXME, we only do forwards */ + /* FIXME, we only do forwards playback, no trick modes yet */ res = gst_element_seek (priv->pipeline, 1.0, GST_FORMAT_TIME, flags, start_type, start, stop_type, stop); @@ -2136,49 +2189,17 @@ preroll_failed: } } -/** - * gst_rtsp_media_prepare: - * @media: a #GstRTSPMedia - * @thread: (transfer full): a #GstRTSPThread to run the bus handler or %NULL - * - * Prepare @media for streaming. This function will create the objects - * to manage the streaming. A pipeline must have been set on @media with - * gst_rtsp_media_take_pipeline(). - * - * It will preroll the pipeline and collect vital information about the streams - * such as the duration. - * - * Returns: %TRUE on success. - */ -gboolean -gst_rtsp_media_prepare (GstRTSPMedia * media, GstRTSPThread * thread) +static gboolean +default_prepare (GstRTSPMedia * media, GstRTSPThread * thread) { GstRTSPMediaPrivate *priv; - GstBus *bus; - GSource *source; GstRTSPMediaClass *klass; + GstBus *bus; GMainContext *context; - - g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE); + GSource *source; priv = media->priv; - g_rec_mutex_lock (&priv->state_lock); - priv->prepare_count++; - - if (priv->status == GST_RTSP_MEDIA_STATUS_PREPARED || - priv->status == GST_RTSP_MEDIA_STATUS_SUSPENDED) - goto was_prepared; - - if (priv->status == GST_RTSP_MEDIA_STATUS_PREPARING) - goto wait_status; - - if (priv->status != GST_RTSP_MEDIA_STATUS_UNPREPARED) - goto not_unprepared; - - if (!priv->reusable && priv->reused) - goto is_reused; - klass = GST_RTSP_MEDIA_GET_CLASS (media); if (!klass->create_rtpbin) @@ -2199,18 +2220,9 @@ gst_rtsp_media_prepare (GstRTSPMedia * media, GstRTSPThread * thread) if (priv->rtpbin == NULL) goto no_rtpbin; - GST_INFO ("preparing media %p", media); - - /* reset some variables */ - priv->is_live = FALSE; - priv->seekable = FALSE; - priv->buffering = FALSE; priv->thread = thread; context = (thread != NULL) ? (thread->context) : NULL; - /* we're preparing now */ - gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING); - bus = gst_pipeline_get_bus (GST_PIPELINE_CAST (priv->pipeline)); /* add the pipeline bus to our custom mainloop */ @@ -2231,6 +2243,80 @@ gst_rtsp_media_prepare (GstRTSPMedia * media, GstRTSPThread * thread) g_source_attach (source, context); g_source_unref (source); + return TRUE; + + /* ERRORS */ +no_create_rtpbin: + { + GST_ERROR ("no create_rtpbin function"); + g_critical ("no create_rtpbin vmethod function set"); + return FALSE; + } +no_rtpbin: + { + GST_WARNING ("no rtpbin element"); + g_warning ("failed to create element 'rtpbin', check your installation"); + return FALSE; + } +} + +/** + * gst_rtsp_media_prepare: + * @media: a #GstRTSPMedia + * @thread: (transfer full) (allow-none): a #GstRTSPThread to run the + * bus handler or %NULL + * + * Prepare @media for streaming. This function will create the objects + * to manage the streaming. A pipeline must have been set on @media with + * gst_rtsp_media_take_pipeline(). + * + * It will preroll the pipeline and collect vital information about the streams + * such as the duration. + * + * Returns: %TRUE on success. + */ +gboolean +gst_rtsp_media_prepare (GstRTSPMedia * media, GstRTSPThread * thread) +{ + GstRTSPMediaPrivate *priv; + GstRTSPMediaClass *klass; + + g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE); + + priv = media->priv; + + g_rec_mutex_lock (&priv->state_lock); + priv->prepare_count++; + + if (priv->status == GST_RTSP_MEDIA_STATUS_PREPARED || + priv->status == GST_RTSP_MEDIA_STATUS_SUSPENDED) + goto was_prepared; + + if (priv->status == GST_RTSP_MEDIA_STATUS_PREPARING) + goto is_preparing; + + if (priv->status != GST_RTSP_MEDIA_STATUS_UNPREPARED) + goto not_unprepared; + + if (!priv->reusable && priv->reused) + goto is_reused; + + GST_INFO ("preparing media %p", media); + + /* reset some variables */ + priv->is_live = FALSE; + priv->seekable = FALSE; + priv->buffering = FALSE; + + /* we're preparing now */ + gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING); + + klass = GST_RTSP_MEDIA_GET_CLASS (media); + if (klass->prepare) { + if (!klass->prepare (media, thread)) + goto prepare_failed; + } + wait_status: g_rec_mutex_unlock (&priv->state_lock); @@ -2246,6 +2332,13 @@ wait_status: return TRUE; /* OK */ +is_preparing: + { + /* we are not going to use the giving thread, so stop it. */ + if (thread) + gst_rtsp_thread_stop (thread); + goto wait_status; + } was_prepared: { GST_LOG ("media %p was prepared", media); @@ -2258,6 +2351,9 @@ was_prepared: /* ERRORS */ not_unprepared: { + /* we are not going to use the giving thread, so stop it. */ + if (thread) + gst_rtsp_thread_stop (thread); GST_WARNING ("media %p was not unprepared", media); priv->prepare_count--; g_rec_mutex_unlock (&priv->state_lock); @@ -2265,25 +2361,22 @@ not_unprepared: } is_reused: { + /* we are not going to use the giving thread, so stop it. */ + if (thread) + gst_rtsp_thread_stop (thread); priv->prepare_count--; g_rec_mutex_unlock (&priv->state_lock); GST_WARNING ("can not reuse media %p", media); return FALSE; } -no_create_rtpbin: - { - priv->prepare_count--; - g_rec_mutex_unlock (&priv->state_lock); - GST_ERROR ("no create_rtpbin function"); - g_critical ("no create_rtpbin vmethod function set"); - return FALSE; - } -no_rtpbin: +prepare_failed: { + /* we are not going to use the giving thread, so stop it. */ + if (thread) + gst_rtsp_thread_stop (thread); priv->prepare_count--; g_rec_mutex_unlock (&priv->state_lock); - GST_WARNING ("no rtpbin element"); - g_warning ("failed to create element 'rtpbin', check your installation"); + GST_ERROR ("failed to prepare media"); return FALSE; } preroll_failed: @@ -2526,7 +2619,7 @@ not_prepared: /** * gst_rtsp_media_get_time_provider: * @media: a #GstRTSPMedia - * @address: an address or %NULL + * @address: (allow-none): an address or %NULL * @port: a port or 0 * * Get the #GstNetTimeProvider for the clock used by @media. The time provider @@ -2620,6 +2713,60 @@ no_setup_sdp: } } +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 @@ -2636,7 +2783,7 @@ gboolean gst_rtsp_media_suspend (GstRTSPMedia * media) { GstRTSPMediaPrivate *priv = media->priv; - GstStateChangeReturn ret; + GstRTSPMediaClass *klass; g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE); @@ -2650,27 +2797,12 @@ gst_rtsp_media_suspend (GstRTSPMedia * media) 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); - 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; - break; - default: - break; + klass = GST_RTSP_MEDIA_GET_CLASS (media); + if (klass->suspend) { + if (!klass->suspend (media)) + goto suspend_failed; } - /* let the streams do the state changes freely, if any */ - media_streams_set_blocked (media, FALSE); + gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_SUSPENDED); done: g_rec_mutex_unlock (&priv->state_lock); @@ -2684,35 +2816,21 @@ not_prepared: GST_WARNING ("media %p was not prepared", media); return FALSE; } -state_failed: +suspend_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); + GST_WARNING ("failed to suspend 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. - */ +/* call with state_lock */ gboolean -gst_rtsp_media_unsuspend (GstRTSPMedia * media) +default_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: gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARED); @@ -2735,17 +2853,13 @@ gst_rtsp_media_unsuspend (GstRTSPMedia * media) 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: @@ -2755,6 +2869,48 @@ preroll_failed: } } +/** + * 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)