diff --git a/gst-libs/gst/rtp/gstrtpbasedepayload.c b/gst-libs/gst/rtp/gstrtpbasedepayload.c index 415899a74c..508395b242 100644 --- a/gst-libs/gst/rtp/gstrtpbasedepayload.c +++ b/gst-libs/gst/rtp/gstrtpbasedepayload.c @@ -39,6 +39,7 @@ struct _GstRTPBaseDepayloadPrivate GstClockTime npt_stop; gdouble play_speed; gdouble play_scale; + guint clock_base; gboolean discont; GstClockTime pts; @@ -52,6 +53,7 @@ struct _GstRTPBaseDepayloadPrivate gboolean negotiated; GstCaps *last_caps; + GstEvent *segment_event; }; /* Filter signals and args */ @@ -92,6 +94,8 @@ static void gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass * klass); static void gst_rtp_base_depayload_init (GstRTPBaseDepayload * rtpbasepayload, GstRTPBaseDepayloadClass * klass); +static GstEvent *create_segment_event (GstRTPBaseDepayload * filter, + guint rtptime, GstClockTime position); GType gst_rtp_base_depayload_get_type (void) @@ -238,6 +242,7 @@ gst_rtp_base_depayload_init (GstRTPBaseDepayload * filter, priv->npt_stop = -1; priv->play_speed = 1.0; priv->play_scale = 1.0; + priv->clock_base = -1; priv->dts = -1; priv->pts = -1; priv->duration = -1; @@ -306,6 +311,12 @@ gst_rtp_base_depayload_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps) else priv->play_scale = 1.0; + value = gst_structure_get_value (caps_struct, "clock-base"); + if (value && G_VALUE_HOLDS_UINT (value)) + priv->clock_base = g_value_get_uint (value); + else + priv->clock_base = -1; + if (bclass->set_caps) { res = bclass->set_caps (filter, caps); if (!res) { @@ -422,6 +433,13 @@ gst_rtp_base_depayload_chain (GstPad * pad, GstObject * parent, GstBuffer * in) } } + /* prepare segment event if needed */ + if (filter->need_newsegment) { + priv->segment_event = create_segment_event (filter, rtptime, + GST_BUFFER_PTS (in)); + filter->need_newsegment = FALSE; + } + bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter); if (G_UNLIKELY (bclass->process == NULL)) @@ -487,6 +505,7 @@ gst_rtp_base_depayload_handle_event (GstRTPBaseDepayload * filter, gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED); filter->need_newsegment = TRUE; filter->priv->next_seqnum = -1; + gst_event_replace (&filter->priv->segment_event, NULL); break; case GST_EVENT_CAPS: { @@ -560,30 +579,46 @@ gst_rtp_base_depayload_handle_sink_event (GstPad * pad, GstObject * parent, } static GstEvent * -create_segment_event (GstRTPBaseDepayload * filter, GstClockTime position) +create_segment_event (GstRTPBaseDepayload * filter, guint rtptime, + GstClockTime position) { GstEvent *event; - GstClockTime stop, running_time; + GstClockTime start, stop, running_time; GstRTPBaseDepayloadPrivate *priv; GstSegment segment; priv = filter->priv; + /* determin the start of the segment */ + start = 0; + if (priv->clock_base != -1 && position != -1) { + GstClockTime exttime, gap; + + exttime = priv->clock_base; + gst_rtp_buffer_ext_timestamp (&exttime, rtptime); + gap = gst_util_uint64_scale_int (exttime - priv->clock_base, + filter->clock_rate, GST_SECOND); + + /* account for lost packets */ + if (position > gap) + start = position - gap; + } + + /* determin the stop of the segment */ + stop = -1; if (priv->npt_stop != -1) - stop = position + priv->npt_stop - priv->npt_start; - else - stop = -1; + stop = start + (priv->npt_stop - priv->npt_start); if (position == -1) position = 0; running_time = gst_segment_to_running_time (&filter->segment, - GST_FORMAT_TIME, position); + GST_FORMAT_TIME, start); gst_segment_init (&segment, GST_FORMAT_TIME); segment.rate = priv->play_speed; segment.applied_rate = priv->play_scale; - segment.start = position; + segment.start = start; segment.stop = stop; segment.time = priv->npt_start; segment.position = position; @@ -654,25 +689,9 @@ gst_rtp_base_depayload_prepare_push (GstRTPBaseDepayload * filter, } /* if this is the first buffer send a NEWSEGMENT */ - if (G_UNLIKELY (filter->need_newsegment)) { - GstEvent *event; - GstClockTime pts; - - if (is_list) { - GstBufferList **blist = obj; - GstBuffer *buf = gst_buffer_list_get (*blist, 0); - pts = GST_BUFFER_PTS (buf); - } else { - GstBuffer **buf = obj; - set_headers (buf, 0, &data); - pts = GST_BUFFER_PTS (*buf); - } - - event = create_segment_event (filter, pts); - - gst_pad_push_event (filter->srcpad, event); - - filter->need_newsegment = FALSE; + if (G_UNLIKELY (filter->priv->segment_event)) { + gst_pad_push_event (filter->srcpad, filter->priv->segment_event); + filter->priv->segment_event = NULL; GST_DEBUG_OBJECT (filter, "Pushed newsegment event on this first buffer"); } @@ -778,9 +797,11 @@ gst_rtp_base_depayload_change_state (GstElement * element, priv->npt_stop = -1; priv->play_speed = 1.0; priv->play_scale = 1.0; + priv->clock_base = -1; priv->next_seqnum = -1; priv->negotiated = FALSE; priv->discont = FALSE; + gst_event_replace (&filter->priv->segment_event, NULL); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; diff --git a/tests/check/libs/rtpbasedepayload.c b/tests/check/libs/rtpbasedepayload.c index 05e4e582e4..2811070517 100644 --- a/tests/check/libs/rtpbasedepayload.c +++ b/tests/check/libs/rtpbasedepayload.c @@ -253,6 +253,11 @@ validate_event (guint index, const gchar * name, const gchar * field, ...) const GstSegment *segment; gst_event_parse_segment (event, &segment); fail_unless_equals_uint64 (segment->rate, expected); + } else if (!g_strcmp0 (field, "base")) { + GstClockTime expected = va_arg (var_args, GstClockTime); + const GstSegment *segment; + gst_event_parse_segment (event, &segment); + fail_unless_equals_uint64 (segment->base, expected); } else if (!g_strcmp0 (field, "media-type")) { const gchar *expected = va_arg (var_args, const gchar *); GstCaps *caps; @@ -292,6 +297,15 @@ validate_event (guint index, const gchar * name, const gchar * field, ...) fail_unless (gst_structure_get_double (gst_caps_get_structure (caps, 0), "play-scale", &scale)); fail_unless (scale == expected); + } else if (!g_strcmp0 (field, "clock-base")) { + guint expected = va_arg (var_args, guint); + GstCaps *caps; + guint clock_base; + gst_event_parse_caps (event, &caps); + fail_unless (gst_structure_get_uint (gst_caps_get_structure (caps, 0), + "clock-base", &clock_base)); + fail_unless (clock_base == expected); + } else { fail ("test cannot validate unknown event field '%s'", field); } @@ -933,8 +947,8 @@ GST_START_TEST (rtp_base_depayload_npt_test) validate_event (6, "segment", "time", G_GUINT64_CONSTANT (1234), - "start", GST_SECOND, - "stop", GST_SECOND + G_GUINT64_CONSTANT (4321 - 1234), NULL); + "start", G_GUINT64_CONSTANT (0), + "stop", G_GUINT64_CONSTANT (4321 - 1234), NULL); destroy_depayloader (state); } @@ -994,7 +1008,7 @@ GST_START_TEST (rtp_base_depayload_play_scale_test) validate_event (6, "segment", "time", G_GUINT64_CONSTANT (0), - "start", GST_SECOND, + "start", G_GUINT64_CONSTANT (0), "stop", G_MAXUINT64, "rate", 1.0, "applied-rate", 2.0, NULL); destroy_depayloader (state); @@ -1055,12 +1069,78 @@ GST_START_TEST (rtp_base_depayload_play_speed_test) validate_event (6, "segment", "time", G_GUINT64_CONSTANT (0), - "start", GST_SECOND, + "start", G_GUINT64_CONSTANT (0), "stop", G_MAXUINT64, "rate", 2.0, "applied-rate", 1.0, NULL); destroy_depayloader (state); } +GST_END_TEST +/* when a depayloader receives new caps events with npt-start, npt-stop and + * clock-base it should save these timestamps as they should affect the next + * segment event being pushed by the depayloader. the produce segment should + * make the positon of the stream reflect the postion form clock-base instead + * of reflecting the running time (for RTSP). + */ +GST_START_TEST (rtp_base_depayload_clock_base_test) +{ + State *state; + + state = create_depayloader ("application/x-rtp", NULL); + + set_state (state, GST_STATE_PLAYING); + + push_rtp_buffer (state, + "pts", 0 * GST_SECOND, + "rtptime", G_GUINT64_CONSTANT (1234), "seq", 0x4242, NULL); + + reconfigure_caps (state, + "application/x-rtp, npt-start=(guint64)1234, npt-stop=(guint64)4321, clock-base=(guint)1234"); + + flush_pipeline (state); + + push_rtp_buffer (state, + "pts", 1 * GST_SECOND, + "rtptime", 1234 + DEFAULT_CLOCK_RATE, + "seq", 0x4242 + 1, NULL); + + set_state (state, GST_STATE_NULL); + + validate_buffers_received (2); + + validate_buffer (0, "pts", 0 * GST_SECOND, "discont", FALSE, NULL); + + validate_buffer (1, "pts", 1 * GST_SECOND, "discont", FALSE, NULL); + + validate_events_received (7); + + validate_event (0, "stream-start", NULL); + + validate_event (1, "caps", "media-type", "application/x-rtp", NULL); + + validate_event (2, "segment", + "time", G_GUINT64_CONSTANT (0), + "start", G_GUINT64_CONSTANT (0), "stop", G_MAXUINT64, NULL); + + validate_event (3, "caps", + "media-type", "application/x-rtp", + "npt-start", G_GUINT64_CONSTANT (1234), + "npt-stop", G_GUINT64_CONSTANT (4321), + "clock-base", 1234, NULL); + + validate_event (4, "flush-start", NULL); + + validate_event (5, "flush-stop", NULL); + + validate_event (6, "segment", + "time", G_GUINT64_CONSTANT (1234), + "start", GST_SECOND, + "stop", GST_SECOND + G_GUINT64_CONSTANT (4321 - 1234), + "base", GST_SECOND, NULL); + + destroy_depayloader (state); +} + GST_END_TEST static Suite * rtp_basepayloading_suite (void) { @@ -1085,6 +1165,7 @@ rtp_basepayloading_suite (void) tcase_add_test (tc_chain, rtp_base_depayload_npt_test); tcase_add_test (tc_chain, rtp_base_depayload_play_scale_test); tcase_add_test (tc_chain, rtp_base_depayload_play_speed_test); + tcase_add_test (tc_chain, rtp_base_depayload_clock_base_test); return s; }