/* Dynamic element handling */
guint nb_dynamic_elements;
guint no_more_pads_pending;
+ gboolean expected_async_done;
};
#define DEFAULT_SHARED FALSE
static gboolean wait_preroll (GstRTSPMedia * media);
-static GstElement *find_payload_element (GstElement * payloader);
+static GstElement *find_payload_element (GstElement * payloader, GstPad * pad);
static guint gst_rtsp_media_signals[SIGNAL_LAST] = { 0 };
#define C_ENUM(v) ((gint) v)
+#define TRICKMODE_FLAGS (GST_SEEK_FLAG_TRICKMODE | GST_SEEK_FLAG_TRICKMODE_KEY_UNITS | GST_SEEK_FLAG_TRICKMODE_FORWARD_PREDICTED)
+
GType
gst_rtsp_suspend_mode_get_type (void)
{
priv->max_mcast_ttl = DEFAULT_MAX_MCAST_TTL;
priv->bind_mcast_address = DEFAULT_BIND_MCAST_ADDRESS;
priv->do_rate_control = DEFAULT_DO_RATE_CONTROL;
+ priv->expected_async_done = FALSE;
}
static void
pad = gst_element_get_static_pad (elem, "src");
/* find the real payload element in case elem is a GstBin */
- pay = find_payload_element (elem);
+ pay = find_payload_element (elem, pad);
/* create the stream */
if (pay == NULL) {
g_mutex_lock (&priv->lock);
idx = priv->streams->len;
- GST_DEBUG ("media %p: creating stream with index %d", media, idx);
+ GST_DEBUG ("media %p: creating stream with index %d and payloader %"
+ GST_PTR_FORMAT, media, idx, payloader);
if (GST_PAD_IS_SRC (pad))
name = g_strdup_printf ("src_%u", idx);
{
GstRTSPMediaPrivate *priv;
GstRTSPStream *stream;
+ gdouble save_rate, save_applied_rate;
gboolean result = TRUE;
+ gboolean first_stream = TRUE;
+ gint i;
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
g_mutex_lock (&priv->lock);
g_assert (priv->streams->len > 0);
- stream = g_ptr_array_index (priv->streams, 0);
- if (!gst_rtsp_stream_get_rates (stream, rate, applied_rate)) {
+ for (i = 0; i < priv->streams->len; i++) {
+ stream = g_ptr_array_index (priv->streams, i);
+ if (gst_rtsp_stream_is_complete (stream)) {
+ if (gst_rtsp_stream_get_rates (stream, rate, applied_rate)) {
+ if (first_stream) {
+ save_rate = *rate;
+ save_applied_rate = *applied_rate;
+ first_stream = FALSE;
+ } else {
+ if (save_rate != *rate || save_applied_rate != *applied_rate) {
+ /* diffrent rate or applied_rate, weird */
+ g_assert (FALSE);
+ result = FALSE;
+ break;
+ }
+ }
+ } else {
+ /* complete stream withot rate and applied_rate, weird */
+ g_assert (FALSE);
+ result = FALSE;
+ break;
+ }
+ }
+ }
+
+ if (!result) {
GST_WARNING_OBJECT (media,
- "failed to obtain rate and applied_rate from first stream");
- result = FALSE;
+ "failed to obtain consistent rate and applied_rate");
}
g_mutex_unlock (&priv->lock);
static void
stream_unblock (GstRTSPStream * stream, GstRTSPMedia * media)
{
- gst_rtsp_stream_unblock_linked (stream);
+ gst_rtsp_stream_set_blocked (stream, FALSE);
}
static void
-media_unblock_linked (GstRTSPMedia * media)
+media_unblock (GstRTSPMedia * media)
{
GstRTSPMediaPrivate *priv = media->priv;
- GST_DEBUG ("media %p unblocking linked streams", media);
+ GST_DEBUG ("media %p unblocking streams", media);
/* media is not blocked any longer, as it contains active streams,
* streams that are complete */
priv->blocked = FALSE;
if (stop != GST_CLOCK_TIME_NONE)
stop_type = GST_SEEK_TYPE_SET;
- /* we force a seek if any seek flag is set, or if the the rate
+ /* we force a seek if any trickmode flag is set, or if the rate
* is non-standard, i.e. not 1.0 */
- force_seek = flags != GST_SEEK_FLAG_NONE || rate != 1.0;
+ force_seek = (flags & TRICKMODE_FLAGS) || rate != 1.0;
if (start != GST_CLOCK_TIME_NONE || stop != GST_CLOCK_TIME_NONE || force_seek) {
- gboolean had_flags = flags != GST_SEEK_FLAG_NONE;
-
GST_INFO ("seeking to %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
GST_TIME_ARGS (current_position));
start = current_position;
start_type = GST_SEEK_TYPE_SET;
- if (!had_flags)
- flags |= GST_SEEK_FLAG_ACCURATE;
}
- } else {
- /* only set keyframe flag when modifying start */
- if (start_type != GST_SEEK_TYPE_NONE)
- if (!had_flags)
- flags |= GST_SEEK_FLAG_KEY_UNIT;
}
if (start == current_position && stop_type == GST_SEEK_TYPE_NONE &&
GstEvent *seek_event;
gboolean unblock = FALSE;
+ /* Handle expected async-done before waiting on next async-done.
+ *
+ * Since the seek further down in code will cause a preroll and
+ * a async-done will be generated it's important to wait on async-done
+ * if that is expected. Otherwise there is the risk that the waiting
+ * for async-done after the seek is detecting the expected async-done
+ * instead of the one that corresponds to the seek. Then execution
+ * continue and act as if the pipeline is prerolled, but it's not.
+ *
+ * During wait_preroll message GST_MESSAGE_ASYNC_DONE will come
+ * and then the state will change from preparing to prepared */
+ if (priv->expected_async_done) {
+ GST_DEBUG (" expected to get async-done, waiting ");
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
+ g_rec_mutex_unlock (&priv->state_lock);
+
+ /* wait until pipeline is prerolled */
+ if (!wait_preroll (media))
+ goto preroll_failed_expected_async_done;
+
+ g_rec_mutex_lock (&priv->state_lock);
+ GST_DEBUG (" got expected async-done");
+ }
+
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
if (rate < 0.0) {
GST_WARNING ("failed to preroll after seek");
return FALSE;
}
+preroll_failed_expected_async_done:
+ {
+ GST_WARNING ("failed to preroll");
+ return FALSE;
+ }
}
/**
* @media must be prepared with gst_rtsp_media_prepare().
*
* Returns: %TRUE on success.
+ * Since: 1.18
*/
gboolean
gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
case GST_MESSAGE_STREAM_STATUS:
break;
case GST_MESSAGE_ASYNC_DONE:
+ if (priv->expected_async_done)
+ priv->expected_async_done = FALSE;
if (priv->complete) {
/* receive the final ASYNC_DONE, that is posted by the media pipeline
* after all the transport parts have been successfully added to
g_object_unref (media);
}
+static gboolean
+is_payloader (GstElement * element)
+{
+ GstElementClass *eclass = GST_ELEMENT_GET_CLASS (element);
+ const gchar *klass;
+
+ klass = gst_element_class_get_metadata (eclass, GST_ELEMENT_METADATA_KLASS);
+ if (klass == NULL)
+ return FALSE;
+
+ if (strstr (klass, "Payloader") && strstr (klass, "RTP")) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static GstElement *
-find_payload_element (GstElement * payloader)
+find_payload_element (GstElement * payloader, GstPad * pad)
{
GstElement *pay = NULL;
if (GST_IS_BIN (payloader)) {
GstIterator *iter;
GValue item = { 0 };
+ gchar *pad_name, *payloader_name;
+ GstElement *element;
+
+ if ((element = gst_bin_get_by_name (GST_BIN (payloader), "pay"))) {
+ if (is_payloader (element))
+ return element;
+ gst_object_unref (element);
+ }
+
+ pad_name = gst_object_get_name (GST_OBJECT (pad));
+ payloader_name = g_strdup_printf ("pay_%s", pad_name);
+ g_free (pad_name);
+ if ((element = gst_bin_get_by_name (GST_BIN (payloader), payloader_name))) {
+ g_free (payloader_name);
+ if (is_payloader (element))
+ return element;
+ gst_object_unref (element);
+ } else {
+ g_free (payloader_name);
+ }
iter = gst_bin_iterate_recurse (GST_BIN (payloader));
while (gst_iterator_next (iter, &item) == GST_ITERATOR_OK) {
- GstElement *element = (GstElement *) g_value_get_object (&item);
- GstElementClass *eclass = GST_ELEMENT_GET_CLASS (element);
- const gchar *klass;
+ element = (GstElement *) g_value_get_object (&item);
- klass =
- gst_element_class_get_metadata (eclass, GST_ELEMENT_METADATA_KLASS);
- if (klass == NULL)
- continue;
-
- if (strstr (klass, "Payloader") && strstr (klass, "RTP")) {
+ if (is_payloader (element)) {
pay = gst_object_ref (element);
g_value_unset (&item);
break;
GstElement *pay;
/* find the real payload element */
- pay = find_payload_element (element);
+ pay = find_payload_element (element, pad);
stream = gst_rtsp_media_create_stream (media, pay, pad);
gst_object_unref (pay);
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
/* at this point the media pipeline has been updated and contain all
* specific transport parts: all active streams contain at least one sink
- * element and it's safe to unblock any blocked streams that are active */
- media_unblock_linked (media);
+ * element and it's safe to unblock all blocked streams */
+ media_unblock (media);
} else {
/* streams are not blocked and media is suspended from PAUSED */
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARED);
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
/* at this point the media pipeline has been updated and contain all
* specific transport parts: all active streams contain at least one sink
- * element and it's safe to unblock any blocked streams that are active */
- media_unblock_linked (media);
+ * element and it's safe to unblock all blocked streams */
+ media_unblock (media);
if (!start_preroll (media))
goto start_failed;
media_set_pipeline_state_locked (GstRTSPMedia * media, GstState state)
{
GstRTSPMediaPrivate *priv = media->priv;
+ GstStateChangeReturn set_state_ret;
+ priv->expected_async_done = FALSE;
if (state == GST_STATE_NULL) {
gst_rtsp_media_unprepare (media);
} else {
if (state == GST_STATE_PLAYING)
/* make sure pads are not blocking anymore when going to PLAYING */
- media_unblock_linked (media);
-
- set_state (media, state);
+ media_unblock (media);
- /* and suspend after pause */
- if (state == GST_STATE_PAUSED)
+ if (state == GST_STATE_PAUSED) {
+ set_state_ret = set_state (media, state);
+ if (set_state_ret == GST_STATE_CHANGE_ASYNC)
+ priv->expected_async_done = TRUE;
+ /* and suspend after pause */
gst_rtsp_media_suspend (media);
+ } else {
+ set_state (media, state);
+ }
}
}
}
}
/**
+ * gst_rtsp_media_has_completed_sender:
+ *
+ * See gst_rtsp_stream_is_complete(), gst_rtsp_stream_is_sender().
+ *
+ * Returns: whether @media has at least one complete sender stream.
+ * Since: 1.18
+ */
+gboolean
+gst_rtsp_media_has_completed_sender (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+ gboolean sender = FALSE;
+ guint i;
+
+ g_mutex_lock (&priv->lock);
+ for (i = 0; i < priv->streams->len; i++) {
+ GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
+ if (gst_rtsp_stream_is_complete (stream))
+ if (gst_rtsp_stream_is_sender (stream) ||
+ !gst_rtsp_stream_is_receiver (stream)) {
+ sender = TRUE;
+ break;
+ }
+ }
+ g_mutex_unlock (&priv->lock);
+
+ return sender;
+}
+
+/**
* gst_rtsp_media_set_rate_control:
*
* Define whether @media will follow the Rate-Control=no behaviour as specified