rtpmanager: Take into account packet rate for max-dropout and max-misorder calculations

https://bugzilla.gnome.org/show_bug.cgi?id=751311
This commit is contained in:
Miguel París Díaz 2015-10-07 13:03:02 +02:00 committed by Sebastian Dröge
parent 4c96094fbb
commit f321bfeaf4
5 changed files with 95 additions and 26 deletions

View file

@ -5,6 +5,8 @@
* Copyright 2007 Nokia Corporation * Copyright 2007 Nokia Corporation
* @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk>. * @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk>.
* Copyright 2007 Wim Taymans <wim.taymans@gmail.com> * Copyright 2007 Wim Taymans <wim.taymans@gmail.com>
* Copyright 2015 Kurento (http://kurento.org/)
* @author: Miguel París <mparisdiaz@gmail.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -327,6 +329,7 @@ struct _GstRtpJitterBufferPrivate
guint64 num_rtx_failed; guint64 num_rtx_failed;
gdouble avg_rtx_num; gdouble avg_rtx_num;
guint64 avg_rtx_rtt; guint64 avg_rtx_rtt;
RTPPacketRateCtx packet_rate_ctx;
/* for the jitter */ /* for the jitter */
GstClockTime last_dts; GstClockTime last_dts;
@ -2304,7 +2307,8 @@ compare_buffer_seqnum (GstBuffer * a, GstBuffer * b, gpointer user_data)
static gboolean static gboolean
handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, gboolean future, handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, gboolean future,
GstBuffer * buffer, guint8 pt, guint16 seqnum, gint gap) GstBuffer * buffer, guint8 pt, guint16 seqnum, gint gap, guint max_dropout,
guint max_misorder)
{ {
GstRtpJitterBufferPrivate *priv; GstRtpJitterBufferPrivate *priv;
guint gap_packets_length; guint gap_packets_length;
@ -2346,7 +2350,7 @@ handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, gboolean future,
GST_DEBUG_OBJECT (jitterbuffer, GST_DEBUG_OBJECT (jitterbuffer,
"buffer too %s %d < %d, got 5 consecutive ones - reset", "buffer too %s %d < %d, got 5 consecutive ones - reset",
(future ? "new" : "old"), gap, (future ? "new" : "old"), gap,
(future ? RTP_MAX_DROPOUT : -RTP_MAX_MISORDER)); (future ? max_dropout : -max_misorder));
reset = TRUE; reset = TRUE;
} else if (!all_consecutive) { } else if (!all_consecutive) {
g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL); g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL);
@ -2354,20 +2358,19 @@ handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, gboolean future,
GST_DEBUG_OBJECT (jitterbuffer, GST_DEBUG_OBJECT (jitterbuffer,
"buffer too %s %d < %d, got no 5 consecutive ones - dropping", "buffer too %s %d < %d, got no 5 consecutive ones - dropping",
(future ? "new" : "old"), gap, (future ? "new" : "old"), gap,
(future ? RTP_MAX_DROPOUT : -RTP_MAX_MISORDER)); (future ? max_dropout : -max_misorder));
buffer = NULL; buffer = NULL;
} else { } else {
GST_DEBUG_OBJECT (jitterbuffer, GST_DEBUG_OBJECT (jitterbuffer,
"buffer too %s %d < %d, got %u consecutive ones - waiting", "buffer too %s %d < %d, got %u consecutive ones - waiting",
(future ? "new" : "old"), gap, (future ? "new" : "old"), gap,
(future ? RTP_MAX_DROPOUT : -RTP_MAX_MISORDER), (future ? max_dropout : -max_misorder), gap_packets_length + 1);
gap_packets_length + 1);
buffer = NULL; buffer = NULL;
} }
} else { } else {
GST_DEBUG_OBJECT (jitterbuffer, GST_DEBUG_OBJECT (jitterbuffer,
"buffer too %s %d < %d, first one - waiting", (future ? "new" : "old"), "buffer too %s %d < %d, first one - waiting", (future ? "new" : "old"),
gap, -RTP_MAX_MISORDER); gap, -max_misorder);
g_queue_push_tail (&priv->gap_packets, buffer); g_queue_push_tail (&priv->gap_packets, buffer);
buffer = NULL; buffer = NULL;
} }
@ -2416,6 +2419,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
RTPJitterBufferItem *item; RTPJitterBufferItem *item;
GstMessage *msg = NULL; GstMessage *msg = NULL;
gboolean estimated_dts = FALSE; gboolean estimated_dts = FALSE;
guint32 packet_rate, max_dropout, max_misorder;
jitterbuffer = GST_RTP_JITTER_BUFFER_CAST (parent); jitterbuffer = GST_RTP_JITTER_BUFFER_CAST (parent);
@ -2520,6 +2524,18 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
expected = priv->next_in_seqnum; expected = priv->next_in_seqnum;
packet_rate =
gst_rtp_packet_rate_ctx_update (&priv->packet_rate_ctx, seqnum, rtptime);
max_dropout =
gst_rtp_packet_rate_ctx_get_max_dropout (&priv->packet_rate_ctx,
priv->max_dropout_time);
max_misorder =
gst_rtp_packet_rate_ctx_get_max_misorder (&priv->packet_rate_ctx,
priv->max_misorder_time);
GST_TRACE_OBJECT (jitterbuffer,
"packet_rate: %d, max_dropout: %d, max_misorder: %d", packet_rate,
max_dropout, max_misorder);
/* now check against our expected seqnum */ /* now check against our expected seqnum */
if (G_LIKELY (expected != -1)) { if (G_LIKELY (expected != -1)) {
gint gap; gint gap;
@ -2539,17 +2555,17 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
if (gap < 0) { if (gap < 0) {
/* we received an old packet */ /* we received an old packet */
if (G_UNLIKELY (gap != -1 && gap < -RTP_MAX_MISORDER)) { if (G_UNLIKELY (gap != -1 && gap < -max_misorder)) {
reset = reset =
handle_big_gap_buffer (jitterbuffer, FALSE, buffer, pt, seqnum, handle_big_gap_buffer (jitterbuffer, FALSE, buffer, pt, seqnum,
gap); gap, max_dropout, max_misorder);
buffer = NULL; buffer = NULL;
} else { } else {
GST_DEBUG_OBJECT (jitterbuffer, "old packet received"); GST_DEBUG_OBJECT (jitterbuffer, "old packet received");
} }
} else { } else {
/* new packet, we are missing some packets */ /* new packet, we are missing some packets */
if (G_UNLIKELY (priv->timers->len >= RTP_MAX_DROPOUT)) { if (G_UNLIKELY (priv->timers->len >= max_dropout)) {
/* If we have timers for more than RTP_MAX_DROPOUT packets /* If we have timers for more than RTP_MAX_DROPOUT packets
* pending this means that we have a huge gap overall. We can * pending this means that we have a huge gap overall. We can
* reset the jitterbuffer at this point because there's * reset the jitterbuffer at this point because there's
@ -2558,14 +2574,14 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
* next packet */ * next packet */
GST_WARNING_OBJECT (jitterbuffer, GST_WARNING_OBJECT (jitterbuffer,
"%d pending timers > %d - resetting", priv->timers->len, "%d pending timers > %d - resetting", priv->timers->len,
RTP_MAX_DROPOUT); max_dropout);
reset = TRUE; reset = TRUE;
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
buffer = NULL; buffer = NULL;
} else if (G_UNLIKELY (gap >= RTP_MAX_DROPOUT)) { } else if (G_UNLIKELY (gap >= max_dropout)) {
reset = reset =
handle_big_gap_buffer (jitterbuffer, TRUE, buffer, pt, seqnum, handle_big_gap_buffer (jitterbuffer, TRUE, buffer, pt, seqnum,
gap); gap, max_dropout, max_misorder);
buffer = NULL; buffer = NULL;
} else { } else {
GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap); GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap);
@ -3372,10 +3388,11 @@ wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
GstClockTime timer_timeout = -1; GstClockTime timer_timeout = -1;
gint i, len; gint i, len;
/* If we have a clock, update "now" now with the very latest running time /* If we have a clock, update "now" now with the very
* we have. It is used below when timeouts are triggered to calculate * latest running time we have. If timers are unscheduled below we
* any next possible timeout. If we only update it after waiting for the * otherwise wouldn't update now (it's only updated when timers
* clock, we would give a too old time to the timeout functions. * expire), and also for the very first loop iteration now would
* otherwise always be 0
*/ */
GST_OBJECT_LOCK (jitterbuffer); GST_OBJECT_LOCK (jitterbuffer);
if (GST_ELEMENT_CLOCK (jitterbuffer)) { if (GST_ELEMENT_CLOCK (jitterbuffer)) {
@ -3478,6 +3495,7 @@ wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
} }
if (ret != GST_CLOCK_UNSCHEDULED) { if (ret != GST_CLOCK_UNSCHEDULED) {
now = timer_timeout + MAX (clock_jitter, 0);
GST_DEBUG_OBJECT (jitterbuffer, "sync done, %d, #%d, %" G_GINT64_FORMAT, GST_DEBUG_OBJECT (jitterbuffer, "sync done, %d, #%d, %" G_GINT64_FORMAT,
ret, priv->timer_seqnum, clock_jitter); ret, priv->timer_seqnum, clock_jitter);
} else { } else {

View file

@ -1,5 +1,7 @@
/* GStreamer /* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
* Copyright (C) 2015 Kurento (http://kurento.org/)
* @author: Miguel París <mparisdiaz@gmail.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -908,6 +910,7 @@ get_clock_rate (RTPSource * src, guint8 payload)
GST_DEBUG ("got clock-rate %d", clock_rate); GST_DEBUG ("got clock-rate %d", clock_rate);
src->clock_rate = clock_rate; src->clock_rate = clock_rate;
gst_rtp_packet_rate_ctx_reset (&src->packet_rate_ctx, clock_rate);
} }
return src->clock_rate; return src->clock_rate;
} }
@ -1041,11 +1044,24 @@ update_receiver_stats (RTPSource * src, RTPPacketInfo * pinfo,
guint16 seqnr, expected; guint16 seqnr, expected;
RTPSourceStats *stats; RTPSourceStats *stats;
gint16 delta; gint16 delta;
gint32 packet_rate, max_dropout, max_misorder;
stats = &src->stats; stats = &src->stats;
seqnr = pinfo->seqnum; seqnr = pinfo->seqnum;
packet_rate =
gst_rtp_packet_rate_ctx_update (&src->packet_rate_ctx, pinfo->seqnum,
pinfo->rtptime);
max_dropout =
gst_rtp_packet_rate_ctx_get_max_dropout (&src->packet_rate_ctx,
src->max_dropout_time);
max_misorder =
gst_rtp_packet_rate_ctx_get_max_misorder (&src->packet_rate_ctx,
src->max_misorder_time);
GST_TRACE ("SSRC %08x, packet_rate: %d, max_dropout: %d, max_misorder: %d",
src->ssrc, packet_rate, max_dropout, max_misorder);
if (stats->cycles == -1) { if (stats->cycles == -1) {
GST_DEBUG ("received first packet"); GST_DEBUG ("received first packet");
/* first time we heard of this source */ /* first time we heard of this source */
@ -1092,7 +1108,7 @@ update_receiver_stats (RTPSource * src, RTPPacketInfo * pinfo,
/* unexpected seqnum in probation */ /* unexpected seqnum in probation */
goto probation_seqnum; goto probation_seqnum;
} }
} else if (delta >= 0 && delta < RTP_MAX_DROPOUT) { } else if (delta >= 0 && delta < max_dropout) {
/* Clear bad packets */ /* Clear bad packets */
stats->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ stats->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */
g_queue_foreach (src->packets, (GFunc) gst_buffer_unref, NULL); g_queue_foreach (src->packets, (GFunc) gst_buffer_unref, NULL);
@ -1104,7 +1120,7 @@ update_receiver_stats (RTPSource * src, RTPPacketInfo * pinfo,
stats->cycles += RTP_SEQ_MOD; stats->cycles += RTP_SEQ_MOD;
} }
stats->max_seq = seqnr; stats->max_seq = seqnr;
} else if (delta < -RTP_MAX_MISORDER || delta >= RTP_MAX_DROPOUT) { } else if (delta < -max_misorder || delta >= max_dropout) {
/* the sequence number made a very large jump */ /* the sequence number made a very large jump */
if (seqnr == stats->bad_seq && src->packets->head) { if (seqnr == stats->bad_seq && src->packets->head) {
/* two sequential packets -- assume that the other side /* two sequential packets -- assume that the other side
@ -1120,7 +1136,7 @@ update_receiver_stats (RTPSource * src, RTPPacketInfo * pinfo,
pinfo->data = NULL; pinfo->data = NULL;
goto bad_sequence; goto bad_sequence;
} }
} else { /* delta < 0 && delta >= -RTP_MAX_MISORDER */ } else { /* delta < 0 && delta >= -max_misorder */
/* Clear bad packets */ /* Clear bad packets */
stats->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ stats->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */
g_queue_foreach (src->packets, (GFunc) gst_buffer_unref, NULL); g_queue_foreach (src->packets, (GFunc) gst_buffer_unref, NULL);
@ -1150,7 +1166,9 @@ done:
} }
bad_sequence: bad_sequence:
{ {
GST_WARNING ("unacceptable seqnum received"); GST_WARNING
("unacceptable seqnum received (seqnr %u, delta %d, packet_rate: %d, max_dropout: %d, max_misorder: %d)",
seqnr, delta, packet_rate, max_dropout, max_misorder);
return FALSE; return FALSE;
} }
probation_seqnum: probation_seqnum:

View file

@ -1,5 +1,7 @@
/* GStreamer /* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
* Copyright (C) 2015 Kurento (http://kurento.org/)
* @author: Miguel París <mparisdiaz@gmail.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -174,6 +176,7 @@ struct _RTPSource {
guint64 bytes_received; guint64 bytes_received;
GQueue *packets; GQueue *packets;
RTPPacketRateCtx packet_rate_ctx;
guint32 max_dropout_time; guint32 max_dropout_time;
guint32 max_misorder_time; guint32 max_misorder_time;

View file

@ -1,5 +1,7 @@
/* GStreamer /* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
* Copyright (C) 2015 Kurento (http://kurento.org/)
* @author: Miguel París <mparisdiaz@gmail.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -81,6 +83,27 @@ gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx * ctx)
return ctx->avg_packet_rate; return ctx->avg_packet_rate;
} }
guint32
gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
{
if (time_ms <= 0 || !ctx->probed) {
return RTP_DEF_DROPOUT;
}
return MAX (RTP_MIN_DROPOUT, ctx->avg_packet_rate * time_ms / 1000);
}
guint32
gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
gint32 time_ms)
{
if (time_ms <= 0 || !ctx->probed) {
return RTP_DEF_MISORDER;
}
return MAX (RTP_MIN_MISORDER, ctx->avg_packet_rate * time_ms / 1000);
}
/** /**
* rtp_stats_init_defaults: * rtp_stats_init_defaults:
* @stats: an #RTPSessionStats struct * @stats: an #RTPSessionStats struct

View file

@ -1,5 +1,7 @@
/* GStreamer /* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
* Copyright (C) 2015 Kurento (http://kurento.org/)
* @author: Miguel París <mparisdiaz@gmail.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -183,15 +185,18 @@ typedef struct {
#define RTP_STATS_BYE_TIMEOUT (2 * GST_SECOND) #define RTP_STATS_BYE_TIMEOUT (2 * GST_SECOND)
/* /*
* The maximum number of missing packets we tolerate. These are packets with a * The default and minimum values of the maximum number of missing packets we tolerate.
* sequence number bigger than the last seen packet. * These are packets with asequence number bigger than the last seen packet.
*/ */
#define RTP_MAX_DROPOUT 3000 #define RTP_DEF_DROPOUT 3000
#define RTP_MIN_DROPOUT 30
/* /*
* The maximum number of misordered packets we tolerate. These are packets with * The default and minimum values of the maximum number of misordered packets we tolerate.
* a sequence number smaller than the last seen packet. * These are packets with a sequence number smaller than the last seen packet.
*/ */
#define RTP_MAX_MISORDER 100 #define RTP_DEF_MISORDER 100
#define RTP_MIN_MISORDER 10
/** /**
* RTPPacketRateCtx: * RTPPacketRateCtx:
@ -209,6 +214,8 @@ typedef struct {
void gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, guint32 clock_rate); void gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, guint32 clock_rate);
guint32 gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx *ctx, guint16 seqnum, guint32 ts); guint32 gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx *ctx, guint16 seqnum, guint32 ts);
guint32 gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx *ctx); guint32 gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx *ctx);
guint32 gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx *ctx, gint32 time_ms);
guint32 gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx *ctx, gint32 time_ms);
/** /**
* RTPSessionStats: * RTPSessionStats: