basedepay: Handle initial gaps and no clock-base

When generating segment, we can't assume the first buffer is actually
the first expected one. If it's not, we need to adjust the segment to
start a bit before.

Additionally, we if don't know when the stream is suppose to have
started (no clock-base in caps), it means we need to keep everything in
running time and only rely on jitterbuffer to synchronize.

https://bugzilla.gnome.org/show_bug.cgi?id=635701
This commit is contained in:
Nicolas Dufresne 2015-03-27 16:22:36 -04:00
parent ceb26dd93d
commit b7facbaf22
2 changed files with 132 additions and 30 deletions

View file

@ -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;

View file

@ -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;
}