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:
parent
ceb26dd93d
commit
b7facbaf22
@ -39,6 +39,7 @@ struct _GstRTPBaseDepayloadPrivate
|
|||||||
GstClockTime npt_stop;
|
GstClockTime npt_stop;
|
||||||
gdouble play_speed;
|
gdouble play_speed;
|
||||||
gdouble play_scale;
|
gdouble play_scale;
|
||||||
|
guint clock_base;
|
||||||
|
|
||||||
gboolean discont;
|
gboolean discont;
|
||||||
GstClockTime pts;
|
GstClockTime pts;
|
||||||
@ -52,6 +53,7 @@ struct _GstRTPBaseDepayloadPrivate
|
|||||||
gboolean negotiated;
|
gboolean negotiated;
|
||||||
|
|
||||||
GstCaps *last_caps;
|
GstCaps *last_caps;
|
||||||
|
GstEvent *segment_event;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Filter signals and args */
|
/* Filter signals and args */
|
||||||
@ -92,6 +94,8 @@ static void gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass *
|
|||||||
klass);
|
klass);
|
||||||
static void gst_rtp_base_depayload_init (GstRTPBaseDepayload * rtpbasepayload,
|
static void gst_rtp_base_depayload_init (GstRTPBaseDepayload * rtpbasepayload,
|
||||||
GstRTPBaseDepayloadClass * klass);
|
GstRTPBaseDepayloadClass * klass);
|
||||||
|
static GstEvent *create_segment_event (GstRTPBaseDepayload * filter,
|
||||||
|
guint rtptime, GstClockTime position);
|
||||||
|
|
||||||
GType
|
GType
|
||||||
gst_rtp_base_depayload_get_type (void)
|
gst_rtp_base_depayload_get_type (void)
|
||||||
@ -238,6 +242,7 @@ gst_rtp_base_depayload_init (GstRTPBaseDepayload * filter,
|
|||||||
priv->npt_stop = -1;
|
priv->npt_stop = -1;
|
||||||
priv->play_speed = 1.0;
|
priv->play_speed = 1.0;
|
||||||
priv->play_scale = 1.0;
|
priv->play_scale = 1.0;
|
||||||
|
priv->clock_base = -1;
|
||||||
priv->dts = -1;
|
priv->dts = -1;
|
||||||
priv->pts = -1;
|
priv->pts = -1;
|
||||||
priv->duration = -1;
|
priv->duration = -1;
|
||||||
@ -306,6 +311,12 @@ gst_rtp_base_depayload_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps)
|
|||||||
else
|
else
|
||||||
priv->play_scale = 1.0;
|
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) {
|
if (bclass->set_caps) {
|
||||||
res = bclass->set_caps (filter, caps);
|
res = bclass->set_caps (filter, caps);
|
||||||
if (!res) {
|
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);
|
bclass = GST_RTP_BASE_DEPAYLOAD_GET_CLASS (filter);
|
||||||
|
|
||||||
if (G_UNLIKELY (bclass->process == NULL))
|
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);
|
gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
|
||||||
filter->need_newsegment = TRUE;
|
filter->need_newsegment = TRUE;
|
||||||
filter->priv->next_seqnum = -1;
|
filter->priv->next_seqnum = -1;
|
||||||
|
gst_event_replace (&filter->priv->segment_event, NULL);
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_CAPS:
|
case GST_EVENT_CAPS:
|
||||||
{
|
{
|
||||||
@ -560,30 +579,46 @@ gst_rtp_base_depayload_handle_sink_event (GstPad * pad, GstObject * parent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static GstEvent *
|
static GstEvent *
|
||||||
create_segment_event (GstRTPBaseDepayload * filter, GstClockTime position)
|
create_segment_event (GstRTPBaseDepayload * filter, guint rtptime,
|
||||||
|
GstClockTime position)
|
||||||
{
|
{
|
||||||
GstEvent *event;
|
GstEvent *event;
|
||||||
GstClockTime stop, running_time;
|
GstClockTime start, stop, running_time;
|
||||||
GstRTPBaseDepayloadPrivate *priv;
|
GstRTPBaseDepayloadPrivate *priv;
|
||||||
GstSegment segment;
|
GstSegment segment;
|
||||||
|
|
||||||
priv = filter->priv;
|
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)
|
if (priv->npt_stop != -1)
|
||||||
stop = position + priv->npt_stop - priv->npt_start;
|
stop = start + (priv->npt_stop - priv->npt_start);
|
||||||
else
|
|
||||||
stop = -1;
|
|
||||||
|
|
||||||
if (position == -1)
|
if (position == -1)
|
||||||
position = 0;
|
position = 0;
|
||||||
|
|
||||||
running_time = gst_segment_to_running_time (&filter->segment,
|
running_time = gst_segment_to_running_time (&filter->segment,
|
||||||
GST_FORMAT_TIME, position);
|
GST_FORMAT_TIME, start);
|
||||||
|
|
||||||
gst_segment_init (&segment, GST_FORMAT_TIME);
|
gst_segment_init (&segment, GST_FORMAT_TIME);
|
||||||
segment.rate = priv->play_speed;
|
segment.rate = priv->play_speed;
|
||||||
segment.applied_rate = priv->play_scale;
|
segment.applied_rate = priv->play_scale;
|
||||||
segment.start = position;
|
segment.start = start;
|
||||||
segment.stop = stop;
|
segment.stop = stop;
|
||||||
segment.time = priv->npt_start;
|
segment.time = priv->npt_start;
|
||||||
segment.position = position;
|
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 this is the first buffer send a NEWSEGMENT */
|
||||||
if (G_UNLIKELY (filter->need_newsegment)) {
|
if (G_UNLIKELY (filter->priv->segment_event)) {
|
||||||
GstEvent *event;
|
gst_pad_push_event (filter->srcpad, filter->priv->segment_event);
|
||||||
GstClockTime pts;
|
filter->priv->segment_event = NULL;
|
||||||
|
|
||||||
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;
|
|
||||||
GST_DEBUG_OBJECT (filter, "Pushed newsegment event on this first buffer");
|
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->npt_stop = -1;
|
||||||
priv->play_speed = 1.0;
|
priv->play_speed = 1.0;
|
||||||
priv->play_scale = 1.0;
|
priv->play_scale = 1.0;
|
||||||
|
priv->clock_base = -1;
|
||||||
priv->next_seqnum = -1;
|
priv->next_seqnum = -1;
|
||||||
priv->negotiated = FALSE;
|
priv->negotiated = FALSE;
|
||||||
priv->discont = FALSE;
|
priv->discont = FALSE;
|
||||||
|
gst_event_replace (&filter->priv->segment_event, NULL);
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||||
break;
|
break;
|
||||||
|
@ -253,6 +253,11 @@ validate_event (guint index, const gchar * name, const gchar * field, ...)
|
|||||||
const GstSegment *segment;
|
const GstSegment *segment;
|
||||||
gst_event_parse_segment (event, &segment);
|
gst_event_parse_segment (event, &segment);
|
||||||
fail_unless_equals_uint64 (segment->rate, expected);
|
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")) {
|
} else if (!g_strcmp0 (field, "media-type")) {
|
||||||
const gchar *expected = va_arg (var_args, const gchar *);
|
const gchar *expected = va_arg (var_args, const gchar *);
|
||||||
GstCaps *caps;
|
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),
|
fail_unless (gst_structure_get_double (gst_caps_get_structure (caps, 0),
|
||||||
"play-scale", &scale));
|
"play-scale", &scale));
|
||||||
fail_unless (scale == expected);
|
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 {
|
} else {
|
||||||
fail ("test cannot validate unknown event field '%s'", field);
|
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",
|
validate_event (6, "segment",
|
||||||
"time", G_GUINT64_CONSTANT (1234),
|
"time", G_GUINT64_CONSTANT (1234),
|
||||||
"start", GST_SECOND,
|
"start", G_GUINT64_CONSTANT (0),
|
||||||
"stop", GST_SECOND + G_GUINT64_CONSTANT (4321 - 1234), NULL);
|
"stop", G_GUINT64_CONSTANT (4321 - 1234), NULL);
|
||||||
|
|
||||||
destroy_depayloader (state);
|
destroy_depayloader (state);
|
||||||
}
|
}
|
||||||
@ -994,7 +1008,7 @@ GST_START_TEST (rtp_base_depayload_play_scale_test)
|
|||||||
|
|
||||||
validate_event (6, "segment",
|
validate_event (6, "segment",
|
||||||
"time", G_GUINT64_CONSTANT (0),
|
"time", G_GUINT64_CONSTANT (0),
|
||||||
"start", GST_SECOND,
|
"start", G_GUINT64_CONSTANT (0),
|
||||||
"stop", G_MAXUINT64, "rate", 1.0, "applied-rate", 2.0, NULL);
|
"stop", G_MAXUINT64, "rate", 1.0, "applied-rate", 2.0, NULL);
|
||||||
|
|
||||||
destroy_depayloader (state);
|
destroy_depayloader (state);
|
||||||
@ -1055,12 +1069,78 @@ GST_START_TEST (rtp_base_depayload_play_speed_test)
|
|||||||
|
|
||||||
validate_event (6, "segment",
|
validate_event (6, "segment",
|
||||||
"time", G_GUINT64_CONSTANT (0),
|
"time", G_GUINT64_CONSTANT (0),
|
||||||
"start", GST_SECOND,
|
"start", G_GUINT64_CONSTANT (0),
|
||||||
"stop", G_MAXUINT64, "rate", 2.0, "applied-rate", 1.0, NULL);
|
"stop", G_MAXUINT64, "rate", 2.0, "applied-rate", 1.0, NULL);
|
||||||
|
|
||||||
destroy_depayloader (state);
|
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 *
|
GST_END_TEST static Suite *
|
||||||
rtp_basepayloading_suite (void)
|
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_npt_test);
|
||||||
tcase_add_test (tc_chain, rtp_base_depayload_play_scale_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_play_speed_test);
|
||||||
|
tcase_add_test (tc_chain, rtp_base_depayload_clock_base_test);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user