mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 20:51:13 +00:00
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
2 changed files with 132 additions and 30 deletions
|
@ -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;
|
||||
|
||||
if (priv->npt_stop != -1)
|
||||
stop = position + priv->npt_stop - priv->npt_start;
|
||||
else
|
||||
/* 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 = 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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue