jitterbuffer: add support for retransmission retry

When we didn't receive a packet after requesting retransmission, retry
asking for retransmission for a certain period.
This commit is contained in:
Wim Taymans 2013-08-02 14:54:56 +02:00
parent e9ad5126db
commit 9a13267e85

View file

@ -92,6 +92,8 @@ enum
#define DEFAULT_DO_RETRANSMISSION FALSE #define DEFAULT_DO_RETRANSMISSION FALSE
#define DEFAULT_RTX_DELAY 20 #define DEFAULT_RTX_DELAY 20
#define DEFAULT_RTX_DELAY_REORDER 3 #define DEFAULT_RTX_DELAY_REORDER 3
#define DEFAULT_RTX_RETRY_TIMEOUT 40
#define DEFAULT_RTX_RETRY_PERIOD 200
enum enum
{ {
@ -105,6 +107,8 @@ enum
PROP_DO_RETRANSMISSION, PROP_DO_RETRANSMISSION,
PROP_RTX_DELAY, PROP_RTX_DELAY,
PROP_RTX_DELAY_REORDER, PROP_RTX_DELAY_REORDER,
PROP_RTX_RETRY_TIMEOUT,
PROP_RTX_RETRY_PERIOD,
PROP_LAST PROP_LAST
}; };
@ -150,6 +154,8 @@ struct _GstRtpJitterBufferPrivate
gboolean do_retransmission; gboolean do_retransmission;
gint rtx_delay; gint rtx_delay;
gint rtx_delay_reorder; gint rtx_delay_reorder;
gint rtx_retry_timeout;
gint rtx_retry_period;
/* the last seqnum we pushed out */ /* the last seqnum we pushed out */
guint32 last_popped_seqnum; guint32 last_popped_seqnum;
@ -220,6 +226,8 @@ typedef struct
guint16 seqnum; guint16 seqnum;
TimerType type; TimerType type;
GstClockTime timeout; GstClockTime timeout;
GstClockTime rtx_base;
GstClockTime rtx_retry;
} TimerData; } TimerData;
#define GST_RTP_JITTER_BUFFER_GET_PRIVATE(o) \ #define GST_RTP_JITTER_BUFFER_GET_PRIVATE(o) \
@ -438,6 +446,37 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
"Sending retransmission event when this much reordering (-1 automatic)", "Sending retransmission event when this much reordering (-1 automatic)",
-1, G_MAXUINT, DEFAULT_RTX_DELAY_REORDER, -1, G_MAXUINT, DEFAULT_RTX_DELAY_REORDER,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpJitterBuffer::rtx-retry-timeout:
*
* When no packet has been received after sending a retransmission event
* for this time, retry sending a retransmission event.
*
* When -1 is used, the value will be estimated based on observed round
* trip time.
*
* Since: 1.2
*/
g_object_class_install_property (gobject_class, PROP_RTX_RETRY_TIMEOUT,
g_param_spec_int ("rtx-retry-timeout", "RTX Retry Timeout",
"Retry sending a transmission event after this timeout in "
"ms (-1 automatic)", -1, G_MAXUINT, DEFAULT_RTX_RETRY_TIMEOUT,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpJitterBuffer::rtx-retry-period:
*
* The amount of time to try to get a retransmission.
*
* When -1 is used, the value will be estimated based on the jitterbuffer
* latency and the observed round trip time.
*
* Since: 1.2
*/
g_object_class_install_property (gobject_class, PROP_RTX_RETRY_PERIOD,
g_param_spec_int ("rtx-retry-period", "RTX Retry Period",
"Try to get a retransmission for this many ms "
"(-1 automatic)", -1, G_MAXUINT, DEFAULT_RTX_RETRY_PERIOD,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/** /**
* GstRtpJitterBuffer::request-pt-map: * GstRtpJitterBuffer::request-pt-map:
@ -552,6 +591,8 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer)
priv->do_retransmission = DEFAULT_DO_RETRANSMISSION; priv->do_retransmission = DEFAULT_DO_RETRANSMISSION;
priv->rtx_delay = DEFAULT_RTX_DELAY; priv->rtx_delay = DEFAULT_RTX_DELAY;
priv->rtx_delay_reorder = DEFAULT_RTX_DELAY_REORDER; priv->rtx_delay_reorder = DEFAULT_RTX_DELAY_REORDER;
priv->rtx_retry_timeout = DEFAULT_RTX_RETRY_TIMEOUT;
priv->rtx_retry_period = DEFAULT_RTX_RETRY_PERIOD;
priv->timers = g_array_new (FALSE, TRUE, sizeof (TimerData)); priv->timers = g_array_new (FALSE, TRUE, sizeof (TimerData));
priv->jbuf = rtp_jitter_buffer_new (); priv->jbuf = rtp_jitter_buffer_new ();
@ -1556,7 +1597,8 @@ update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
} else if (gap > priv->rtx_delay_reorder) { } else if (gap > priv->rtx_delay_reorder) {
/* max gap, we exceeded the max reorder distance and we don't expect the /* max gap, we exceeded the max reorder distance and we don't expect the
* missing packet to be this reordered */ * missing packet to be this reordered */
reschedule_timer (jitterbuffer, test, test->seqnum, -1); if (test->rtx_retry == 0)
reschedule_timer (jitterbuffer, test, test->seqnum, -1);
} }
} }
@ -1571,6 +1613,9 @@ update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
else else
timer = add_timer (jitterbuffer, TIMER_TYPE_EXPECTED, timer = add_timer (jitterbuffer, TIMER_TYPE_EXPECTED,
priv->next_in_seqnum, expected); priv->next_in_seqnum, expected);
timer->rtx_base = timer->timeout;
timer->rtx_retry = 0;
} else if (timer) { } else if (timer) {
/* if we had a timer, remove it, we don't know when to expect the next /* if we had a timer, remove it, we don't know when to expect the next
* packet. */ * packet. */
@ -2103,6 +2148,25 @@ wait:
} }
} }
/* the timeout for when we expected a packet expired */
static GstFlowReturn
do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
GstClockTimeDiff clock_jitter)
{
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
GST_DEBUG_OBJECT (jitterbuffer, "expected %d didn't arrive", timer->seqnum);
timer->rtx_retry += (priv->rtx_retry_timeout * GST_MSECOND);
if (timer->rtx_retry > (priv->rtx_retry_period * GST_MSECOND))
remove_timer (jitterbuffer, timer);
else
reschedule_timer (jitterbuffer, timer, timer->seqnum,
timer->rtx_base + timer->rtx_retry);
return GST_FLOW_OK;
}
/* a packet is lost */ /* a packet is lost */
static GstFlowReturn static GstFlowReturn
do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer, do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
@ -2298,9 +2362,7 @@ wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
do_timeout: do_timeout:
switch (timer->type) { switch (timer->type) {
case TIMER_TYPE_EXPECTED: case TIMER_TYPE_EXPECTED:
GST_DEBUG_OBJECT (jitterbuffer, "expected %d didn't arrive", result = do_expected_timeout (jitterbuffer, timer, clock_jitter);
timer->seqnum);
remove_timer (jitterbuffer, timer);
break; break;
case TIMER_TYPE_LOST: case TIMER_TYPE_LOST:
result = do_lost_timeout (jitterbuffer, timer, clock_jitter); result = do_lost_timeout (jitterbuffer, timer, clock_jitter);
@ -2740,6 +2802,16 @@ gst_rtp_jitter_buffer_set_property (GObject * object,
priv->rtx_delay_reorder = g_value_get_int (value); priv->rtx_delay_reorder = g_value_get_int (value);
JBUF_UNLOCK (priv); JBUF_UNLOCK (priv);
break; break;
case PROP_RTX_RETRY_TIMEOUT:
JBUF_LOCK (priv);
priv->rtx_retry_timeout = g_value_get_int (value);
JBUF_UNLOCK (priv);
break;
case PROP_RTX_RETRY_PERIOD:
JBUF_LOCK (priv);
priv->rtx_retry_period = g_value_get_int (value);
JBUF_UNLOCK (priv);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -2811,6 +2883,16 @@ gst_rtp_jitter_buffer_get_property (GObject * object,
g_value_set_int (value, priv->rtx_delay_reorder); g_value_set_int (value, priv->rtx_delay_reorder);
JBUF_UNLOCK (priv); JBUF_UNLOCK (priv);
break; break;
case PROP_RTX_RETRY_TIMEOUT:
JBUF_LOCK (priv);
g_value_set_int (value, priv->rtx_retry_timeout);
JBUF_UNLOCK (priv);
break;
case PROP_RTX_RETRY_PERIOD:
JBUF_LOCK (priv);
g_value_set_int (value, priv->rtx_retry_period);
JBUF_UNLOCK (priv);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;