diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c index 7add9d5afe..c85400c40b 100644 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/gst/rtpmanager/gstrtpjitterbuffer.c @@ -304,7 +304,7 @@ struct _GstRtpJitterBufferPrivate /* last output time */ GstClockTime last_out_time; /* last valid input timestamp and rtptime pair */ - GstClockTime ips_dts; + GstClockTime ips_pts; guint64 ips_rtptime; GstClockTime packet_spacing; gint equidistant; @@ -312,7 +312,7 @@ struct _GstRtpJitterBufferPrivate GQueue gap_packets; /* the next expected seqnum we receive */ - GstClockTime last_in_dts; + GstClockTime last_in_pts; guint32 next_in_seqnum; GArray *timers; @@ -1298,7 +1298,7 @@ gst_rtp_jitter_buffer_set_active (GstRtpJitterBuffer * jbuf, gboolean active, } if ((item = rtp_jitter_buffer_peek (priv->jbuf))) { /* head buffer timestamp and offset gives our output time */ - last_out = item->dts + priv->ts_offset; + last_out = item->pts + priv->ts_offset; } else { /* use last known time when the buffer is empty */ last_out = priv->last_out_time; @@ -1554,7 +1554,7 @@ gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer) priv->next_seqnum = -1; priv->seqnum_base = -1; priv->ips_rtptime = -1; - priv->ips_dts = GST_CLOCK_TIME_NONE; + priv->ips_pts = GST_CLOCK_TIME_NONE; priv->packet_spacing = 0; priv->next_in_seqnum = -1; priv->clock_rate = -1; @@ -1566,7 +1566,7 @@ gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer) priv->avg_jitter = 0; priv->last_dts = -1; priv->last_rtptime = -1; - priv->last_in_dts = 0; + priv->last_in_pts = 0; priv->equidistant = 0; GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer"); rtp_jitter_buffer_flush (priv->jbuf, (GFunc) free_item, NULL); @@ -1782,7 +1782,7 @@ queue_event (GstRtpJitterBuffer * jitterbuffer, GstEvent * event) GST_DEBUG_OBJECT (jitterbuffer, "adding event"); item = alloc_item (event, ITEM_TYPE_EVENT, -1, -1, -1, 0, -1); - rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL, -1); + rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL); if (head) JBUF_SIGNAL_EVENT (priv); @@ -2304,8 +2304,8 @@ already_lost (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum) */ static void update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum, - GstClockTime dts, gboolean do_next_seqnum, gboolean is_rtx, - TimerData * timer) + GstClockTime dts, GstClockTime pts, gboolean do_next_seqnum, + gboolean is_rtx, TimerData * timer) { GstRtpJitterBufferPrivate *priv = jitterbuffer->priv; @@ -2351,16 +2351,16 @@ update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum, * several retransmitted packets. For accuracy we should update the * stats also when the redundant retransmitted packets arrives. */ timer_queue_append (priv->rtx_stats_timers, timer, - dts + priv->rtx_stats_timeout * GST_MSECOND, FALSE); + pts + priv->rtx_stats_timeout * GST_MSECOND, FALSE); } } } - if (do_next_seqnum && dts != GST_CLOCK_TIME_NONE) { + if (do_next_seqnum && pts != GST_CLOCK_TIME_NONE) { GstClockTime expected, delay; /* calculate expected arrival time of the next seqnum */ - expected = dts + priv->packet_spacing; + expected = pts + priv->packet_spacing; delay = get_rtx_delay (priv); @@ -2387,16 +2387,16 @@ update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum, static void calculate_packet_spacing (GstRtpJitterBuffer * jitterbuffer, guint32 rtptime, - GstClockTime dts) + GstClockTime pts) { GstRtpJitterBufferPrivate *priv = jitterbuffer->priv; /* we need consecutive seqnums with a different * rtptime to estimate the packet spacing. */ if (priv->ips_rtptime != rtptime) { - /* rtptime changed, check dts diff */ - if (priv->ips_dts != -1 && dts != -1 && dts > priv->ips_dts) { - GstClockTime new_packet_spacing = dts - priv->ips_dts; + /* rtptime changed, check pts diff */ + if (priv->ips_pts != -1 && pts != -1 && pts > priv->ips_pts) { + GstClockTime new_packet_spacing = pts - priv->ips_pts; GstClockTime old_packet_spacing = priv->packet_spacing; /* Biased towards bigger packet spacings to prevent @@ -2421,33 +2421,33 @@ calculate_packet_spacing (GstRtpJitterBuffer * jitterbuffer, guint32 rtptime, GST_TIME_ARGS (priv->packet_spacing)); } priv->ips_rtptime = rtptime; - priv->ips_dts = dts; + priv->ips_pts = pts; } } static void calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected, - guint16 seqnum, GstClockTime dts, gint gap) + guint16 seqnum, GstClockTime pts, gint gap) { GstRtpJitterBufferPrivate *priv = jitterbuffer->priv; - GstClockTime duration, expected_dts, delay; + GstClockTime duration, expected_pts, delay; TimerType type; gboolean equidistant = priv->equidistant > 0; GST_DEBUG_OBJECT (jitterbuffer, - "dts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT, - GST_TIME_ARGS (dts), GST_TIME_ARGS (priv->last_in_dts)); + "pts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT, + GST_TIME_ARGS (pts), GST_TIME_ARGS (priv->last_in_pts)); - if (dts == GST_CLOCK_TIME_NONE) { - GST_WARNING_OBJECT (jitterbuffer, "Have no DTS"); + if (pts == GST_CLOCK_TIME_NONE) { + GST_WARNING_OBJECT (jitterbuffer, "Have no PTS"); return; } if (equidistant) { GstClockTime total_duration; /* the total duration spanned by the missing packets */ - if (dts >= priv->last_in_dts) - total_duration = dts - priv->last_in_dts; + if (pts >= priv->last_in_pts) + total_duration = pts - priv->last_in_pts; else total_duration = 0; @@ -2488,19 +2488,19 @@ calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected, * the timer thread */ if (lost_packets > 0) { add_timer (jitterbuffer, TIMER_TYPE_LOST, expected, lost_packets, - priv->last_in_dts + duration, 0, gap_time); + priv->last_in_pts + duration, 0, gap_time); expected += lost_packets; - priv->last_in_dts += gap_time; + priv->last_in_pts += gap_time; } } - expected_dts = priv->last_in_dts + duration; + expected_pts = priv->last_in_pts + duration; } else { /* If we cannot assume equidistant packet spacing, the only thing we now - * for sure is that the missing packets have expected dts not later than - * the last received dts. */ + * for sure is that the missing packets have expected pts not later than + * the last received pts. */ duration = 0; - expected_dts = dts; + expected_pts = pts; } delay = 0; @@ -2516,20 +2516,20 @@ calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected, GstClockTime timeout = timer->timeout; timer->duration = duration; - if (timeout > (expected_dts + delay) && timer->num_rtx_retry == 0) { - reschedule_timer (jitterbuffer, timer, timer->seqnum, expected_dts, + if (timeout > (expected_pts + delay) && timer->num_rtx_retry == 0) { + reschedule_timer (jitterbuffer, timer, timer->seqnum, expected_pts, delay, TRUE); } expected++; - expected_dts += duration; + expected_pts += duration; } } else { type = TIMER_TYPE_LOST; } while (gst_rtp_buffer_compare_seqnum (expected, seqnum) > 0) { - add_timer (jitterbuffer, type, expected, 0, expected_dts, delay, duration); - expected_dts += duration; + add_timer (jitterbuffer, type, expected, 0, expected_pts, delay, duration); + expected_pts += duration; expected++; } } @@ -2617,13 +2617,13 @@ compare_buffer_seqnum (GstBuffer * a, GstBuffer * b, gpointer user_data) } static gboolean -handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, gboolean future, - GstBuffer * buffer, guint8 pt, guint16 seqnum, gint gap, guint max_dropout, - guint max_misorder) +handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, GstBuffer * buffer, + guint8 pt, guint16 seqnum, gint gap, guint max_dropout, guint max_misorder) { GstRtpJitterBufferPrivate *priv; guint gap_packets_length; gboolean reset = FALSE; + gboolean future = gap > 0; priv = jitterbuffer->priv; @@ -2711,6 +2711,78 @@ get_current_running_time (GstRtpJitterBuffer * jitterbuffer) return running_time; } +static GstFlowReturn +gst_rtp_jitter_buffer_reset (GstRtpJitterBuffer * jitterbuffer, + GstPad * pad, GstObject * parent, guint16 seqnum) +{ + GstRtpJitterBufferPrivate *priv = jitterbuffer->priv; + GstFlowReturn ret = GST_FLOW_OK; + GList *events = NULL, *l; + GList *buffers; + gboolean head; + + GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer"); + rtp_jitter_buffer_flush (priv->jbuf, + (GFunc) free_item_and_retain_events, &events); + rtp_jitter_buffer_reset_skew (priv->jbuf); + remove_all_timers (jitterbuffer); + priv->discont = TRUE; + priv->last_popped_seqnum = -1; + + if (priv->gap_packets.head) { + GstBuffer *gap_buffer = priv->gap_packets.head->data; + GstRTPBuffer gap_rtp = GST_RTP_BUFFER_INIT; + + gst_rtp_buffer_map (gap_buffer, GST_MAP_READ, &gap_rtp); + priv->next_seqnum = gst_rtp_buffer_get_seq (&gap_rtp); + gst_rtp_buffer_unmap (&gap_rtp); + } else { + priv->next_seqnum = seqnum; + } + + priv->last_in_pts = -1; + priv->next_in_seqnum = -1; + + /* Insert all sticky events again in order, otherwise we would + * potentially loose STREAM_START, CAPS or SEGMENT events + */ + events = g_list_reverse (events); + for (l = events; l; l = l->next) { + RTPJitterBufferItem *item; + + item = alloc_item (l->data, ITEM_TYPE_EVENT, -1, -1, -1, 0, -1); + rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL); + } + g_list_free (events); + + JBUF_SIGNAL_EVENT (priv); + + /* reset spacing estimation when gap */ + priv->ips_rtptime = -1; + priv->ips_pts = GST_CLOCK_TIME_NONE; + + buffers = g_list_copy (priv->gap_packets.head); + g_queue_clear (&priv->gap_packets); + + priv->ips_rtptime = -1; + priv->ips_pts = GST_CLOCK_TIME_NONE; + JBUF_UNLOCK (jitterbuffer->priv); + + for (l = buffers; l; l = l->next) { + ret = gst_rtp_jitter_buffer_chain (pad, parent, l->data); + l->data = NULL; + if (ret != GST_FLOW_OK) { + l = l->next; + break; + } + } + for (; l; l = l->next) + gst_buffer_unref (l->data); + g_list_free (buffers); + + return ret; +} + static GstFlowReturn gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) @@ -2730,7 +2802,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, RTPJitterBufferItem *item; GstMessage *msg = NULL; gboolean estimated_dts = FALSE; - guint32 packet_rate, max_dropout, max_misorder; + gint32 packet_rate, max_dropout, max_misorder; TimerData *timer = NULL; jitterbuffer = GST_RTP_JITTER_BUFFER_CAST (parent); @@ -2828,7 +2900,6 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, "packet seqnum #%d before seqnum-base #%d", seqnum, priv->seqnum_base); gst_buffer_unref (buffer); - ret = GST_FLOW_OK; goto finished; } else if (gap > 16384) { /* From now on don't compare against the seqnum base anymore as @@ -2853,156 +2924,90 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, max_dropout, max_misorder); /* now check against our expected seqnum */ - if (G_LIKELY (expected != -1)) { - gint gap; - - /* now calculate gap */ - gap = gst_rtp_buffer_compare_seqnum (expected, seqnum); - - GST_DEBUG_OBJECT (jitterbuffer, "expected #%d, got #%d, gap of %d", - expected, seqnum, gap); - - if (G_LIKELY (gap == 0)) { - /* packet is expected */ - calculate_packet_spacing (jitterbuffer, rtptime, dts); - do_next_seqnum = TRUE; - } else { - gboolean reset = FALSE; - - if (gap < 0) { - /* we received an old packet */ - if (G_UNLIKELY (gap != -1 && gap < -max_misorder)) { - reset = - handle_big_gap_buffer (jitterbuffer, FALSE, buffer, pt, seqnum, - gap, max_dropout, max_misorder); - buffer = NULL; - } else { - GST_DEBUG_OBJECT (jitterbuffer, "old packet received"); - } - } else { - /* new packet, we are missing some packets */ - if (G_UNLIKELY (priv->timers->len >= max_dropout)) { - /* If we have timers for more than RTP_MAX_DROPOUT packets - * pending this means that we have a huge gap overall. We can - * reset the jitterbuffer at this point because there's - * just too much data missing to be able to do anything - * sensible with the past data. Just try again from the - * next packet */ - GST_WARNING_OBJECT (jitterbuffer, - "%d pending timers > %d - resetting", priv->timers->len, - max_dropout); - reset = TRUE; - gst_buffer_unref (buffer); - buffer = NULL; - } else if (G_UNLIKELY (gap >= max_dropout)) { - reset = - handle_big_gap_buffer (jitterbuffer, TRUE, buffer, pt, seqnum, - gap, max_dropout, max_misorder); - buffer = NULL; - } else { - GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap); - /* fill in the gap with EXPECTED timers */ - calculate_expected (jitterbuffer, expected, seqnum, dts, gap); - - do_next_seqnum = TRUE; - } - } - if (G_UNLIKELY (reset)) { - GList *events = NULL, *l; - GList *buffers; - - GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer"); - rtp_jitter_buffer_flush (priv->jbuf, - (GFunc) free_item_and_retain_events, &events); - rtp_jitter_buffer_reset_skew (priv->jbuf); - remove_all_timers (jitterbuffer); - priv->discont = TRUE; - priv->last_popped_seqnum = -1; - - if (priv->gap_packets.head) { - GstBuffer *gap_buffer = priv->gap_packets.head->data; - GstRTPBuffer gap_rtp = GST_RTP_BUFFER_INIT; - - gst_rtp_buffer_map (gap_buffer, GST_MAP_READ, &gap_rtp); - priv->next_seqnum = gst_rtp_buffer_get_seq (&gap_rtp); - gst_rtp_buffer_unmap (&gap_rtp); - } else { - priv->next_seqnum = seqnum; - } - - priv->last_in_dts = -1; - priv->next_in_seqnum = -1; - - /* Insert all sticky events again in order, otherwise we would - * potentially loose STREAM_START, CAPS or SEGMENT events - */ - events = g_list_reverse (events); - for (l = events; l; l = l->next) { - RTPJitterBufferItem *item; - - item = alloc_item (l->data, ITEM_TYPE_EVENT, -1, -1, -1, 0, -1); - rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL, -1); - } - g_list_free (events); - - JBUF_SIGNAL_EVENT (priv); - - /* reset spacing estimation when gap */ - priv->ips_rtptime = -1; - priv->ips_dts = GST_CLOCK_TIME_NONE; - - buffers = g_list_copy (priv->gap_packets.head); - g_queue_clear (&priv->gap_packets); - - priv->ips_rtptime = -1; - priv->ips_dts = GST_CLOCK_TIME_NONE; - JBUF_UNLOCK (jitterbuffer->priv); - - for (l = buffers; l; l = l->next) { - ret = gst_rtp_jitter_buffer_chain (pad, parent, l->data); - l->data = NULL; - if (ret != GST_FLOW_OK) { - l = l->next; - break; - } - } - for (; l; l = l->next) - gst_buffer_unref (l->data); - g_list_free (buffers); - - return ret; - } - /* reset spacing estimation when gap */ - priv->ips_rtptime = -1; - priv->ips_dts = GST_CLOCK_TIME_NONE; - } - } else { + if (G_UNLIKELY (expected == -1)) { GST_DEBUG_OBJECT (jitterbuffer, "First buffer #%d", seqnum); + /* calculate a pts based on rtptime and arrival time (dts) */ + pts = rtp_jitter_buffer_calculate_pts (priv->jbuf, dts, rtptime, + gst_element_get_base_time (GST_ELEMENT_CAST (jitterbuffer))); + /* we don't know what the next_in_seqnum should be, wait for the last * possible moment to push this buffer, maybe we get an earlier seqnum * while we wait */ - set_timer (jitterbuffer, TIMER_TYPE_DEADLINE, seqnum, dts); - do_next_seqnum = TRUE; - /* take rtptime and dts to calculate packet spacing */ - priv->ips_rtptime = rtptime; - priv->ips_dts = dts; - } + set_timer (jitterbuffer, TIMER_TYPE_DEADLINE, seqnum, pts); - /* We had no huge gap, let's drop all the gap packets */ - if (buffer != NULL) { + do_next_seqnum = TRUE; + /* take rtptime and pts to calculate packet spacing */ + priv->ips_rtptime = rtptime; + priv->ips_pts = pts; + + } else { + gint gap; + /* now calculate gap */ + gap = gst_rtp_buffer_compare_seqnum (expected, seqnum); + GST_DEBUG_OBJECT (jitterbuffer, "expected #%d, got #%d, gap of %d", + expected, seqnum, gap); + + if (G_UNLIKELY (gap > 0 && priv->timers->len >= max_dropout)) { + /* If we have timers for more than RTP_MAX_DROPOUT packets + * pending this means that we have a huge gap overall. We can + * reset the jitterbuffer at this point because there's + * just too much data missing to be able to do anything + * sensible with the past data. Just try again from the + * next packet */ + GST_WARNING_OBJECT (jitterbuffer, "%d pending timers > %d - resetting", + priv->timers->len, max_dropout); + gst_buffer_unref (buffer); + return gst_rtp_jitter_buffer_reset (jitterbuffer, pad, parent, seqnum); + } + + /* Special handling of large gaps */ + if ((gap != -1 && gap < -max_misorder) || (gap >= max_dropout)) { + gboolean reset = handle_big_gap_buffer (jitterbuffer, buffer, pt, seqnum, + gap, max_dropout, max_misorder); + if (reset) { + return gst_rtp_jitter_buffer_reset (jitterbuffer, pad, parent, seqnum); + } else { + GST_DEBUG_OBJECT (jitterbuffer, + "Had big gap, waiting for more consecutive packets"); + goto finished; + } + } + + /* We had no huge gap, let's drop all the gap packets */ GST_DEBUG_OBJECT (jitterbuffer, "Clearing gap packets"); g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL); g_queue_clear (&priv->gap_packets); - } else { - GST_DEBUG_OBJECT (jitterbuffer, - "Had big gap, waiting for more consecutive packets"); - JBUF_UNLOCK (jitterbuffer->priv); - return GST_FLOW_OK; + + /* calculate a pts based on rtptime and arrival time (dts) */ + pts = rtp_jitter_buffer_calculate_pts (priv->jbuf, dts, rtptime, + gst_element_get_base_time (GST_ELEMENT_CAST (jitterbuffer))); + + if (G_LIKELY (gap == 0)) { + /* packet is expected */ + calculate_packet_spacing (jitterbuffer, rtptime, pts); + do_next_seqnum = TRUE; + } else { + + /* we have a gap */ + if (gap > 0) { + GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap); + /* fill in the gap with EXPECTED timers */ + calculate_expected (jitterbuffer, expected, seqnum, pts, gap); + do_next_seqnum = TRUE; + } else { + GST_DEBUG_OBJECT (jitterbuffer, "old packet received"); + do_next_seqnum = FALSE; + } + + /* reset spacing estimation when gap */ + priv->ips_rtptime = -1; + priv->ips_pts = GST_CLOCK_TIME_NONE; + } } if (do_next_seqnum) { - priv->last_in_dts = dts; + priv->last_in_pts = pts; priv->next_in_seqnum = (seqnum + 1) & 0xffff; } @@ -3073,23 +3078,22 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, if (estimated_dts) item = alloc_item (buffer, ITEM_TYPE_BUFFER, GST_CLOCK_TIME_NONE, - GST_CLOCK_TIME_NONE, seqnum, 1, rtptime); + pts, seqnum, 1, rtptime); else item = alloc_item (buffer, ITEM_TYPE_BUFFER, dts, pts, seqnum, 1, rtptime); /* now insert the packet into the queue in sorted order. This function returns * FALSE if a packet with the same seqnum was already in the queue, meaning we * have a duplicate. */ - if (G_UNLIKELY (!rtp_jitter_buffer_insert (priv->jbuf, item, - &head, &percent, - gst_element_get_base_time (GST_ELEMENT_CAST (jitterbuffer))))) { + if (G_UNLIKELY (!rtp_jitter_buffer_insert (priv->jbuf, item, &head, + &percent))) { if (GST_BUFFER_IS_RETRANSMISSION (buffer) && timer) update_rtx_stats (jitterbuffer, timer, dts, FALSE); goto duplicate; } /* update timers */ - update_timers (jitterbuffer, seqnum, dts, do_next_seqnum, + update_timers (jitterbuffer, seqnum, dts, pts, do_next_seqnum, GST_BUFFER_IS_RETRANSMISSION (buffer), timer); /* we had an unhandled SR, handle it now */ @@ -3258,7 +3262,7 @@ update_estimated_eos (GstRtpJitterBuffer * jitterbuffer, } /* this is the current time as running-time */ - out_time = item->dts; + out_time = item->pts; if (elapsed > 0) estimated = gst_util_uint64_scale (out_time, total, elapsed); @@ -3731,7 +3735,7 @@ do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer, /* we now only accept seqnum bigger than this */ if (gst_rtp_buffer_compare_seqnum (priv->next_in_seqnum, next_in_seqnum) > 0) { priv->next_in_seqnum = next_in_seqnum; - priv->last_in_dts = apply_offset (jitterbuffer, timer->timeout); + priv->last_in_pts = apply_offset (jitterbuffer, timer->timeout); } /* Avoid creating events if we don't need it. Note that we still need to create @@ -3752,7 +3756,7 @@ do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer, "retry", G_TYPE_UINT, num_rtx_retry, NULL)); } item = alloc_item (event, ITEM_TYPE_LOST, -1, -1, seqnum, lost_packets, -1); - rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL, -1); + rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL); if (GST_CLOCK_TIME_IS_VALID (timer->rtx_last)) { /* Store info to update stats if the packet arrives too late */ @@ -4250,7 +4254,7 @@ gst_rtp_jitter_buffer_sink_query (GstPad * pad, GstObject * parent, RTP_JITTER_BUFFER_MODE_BUFFER) { GST_DEBUG_OBJECT (jitterbuffer, "adding serialized query"); item = alloc_item (query, ITEM_TYPE_QUERY, -1, -1, -1, 0, -1); - rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL, -1); + rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL); if (head) JBUF_SIGNAL_EVENT (priv); JBUF_WAIT_QUERY (priv, out_flushing); diff --git a/gst/rtpmanager/rtpjitterbuffer.c b/gst/rtpmanager/rtpjitterbuffer.c index 0ffbe54d89..c31c8e64bd 100644 --- a/gst/rtpmanager/rtpjitterbuffer.c +++ b/gst/rtpmanager/rtpjitterbuffer.c @@ -688,92 +688,16 @@ queue_do_insert (RTPJitterBuffer * jbuf, GList * list, GList * item) queue->length++; } -/** - * rtp_jitter_buffer_insert: - * @jbuf: an #RTPJitterBuffer - * @item: an #RTPJitterBufferItem to insert - * @head: TRUE when the head element changed. - * @percent: the buffering percent after insertion - * @base_time: base time of the pipeline - * - * Inserts @item into the packet queue of @jbuf. The sequence number of the - * packet will be used to sort the packets. This function takes ownerhip of - * @buf when the function returns %TRUE. - * - * When @head is %TRUE, the new packet was added at the head of the queue and - * will be available with the next call to rtp_jitter_buffer_pop() and - * rtp_jitter_buffer_peek(). - * - * Returns: %FALSE if a packet with the same number already existed. - */ -gboolean -rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item, - gboolean * head, gint * percent, GstClockTime base_time) +GstClockTime +rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, + guint32 rtptime, GstClockTime base_time) { - GList *list, *event = NULL; - guint32 rtptime; guint64 ext_rtptime; - guint16 seqnum; - GstClockTime gstrtptime, dts; + GstClockTime gstrtptime, pts; GstClock *media_clock, *pipeline_clock; guint64 media_clock_offset; gboolean rfc7273_mode; - g_return_val_if_fail (jbuf != NULL, FALSE); - g_return_val_if_fail (item != NULL, FALSE); - - list = jbuf->packets->tail; - - /* no seqnum, simply append then */ - if (item->seqnum == -1) - goto append; - - seqnum = item->seqnum; - - /* loop the list to skip strictly larger seqnum buffers */ - for (; list; list = g_list_previous (list)) { - guint16 qseq; - gint gap; - RTPJitterBufferItem *qitem = (RTPJitterBufferItem *) list; - - if (qitem->seqnum == -1) { - /* keep a pointer to the first consecutive event if not already - * set. we will insert the packet after the event if we can't find - * a packet with lower sequence number before the event. */ - if (event == NULL) - event = list; - continue; - } - - qseq = qitem->seqnum; - - /* compare the new seqnum to the one in the buffer */ - gap = gst_rtp_buffer_compare_seqnum (seqnum, qseq); - - /* we hit a packet with the same seqnum, notify a duplicate */ - if (G_UNLIKELY (gap == 0)) - goto duplicate; - - /* seqnum > qseq, we can stop looking */ - if (G_LIKELY (gap < 0)) - break; - - /* if we've found a packet with greater sequence number, cleanup the - * event pointer as the packet will be inserted before the event */ - event = NULL; - } - - /* if event is set it means that packets before the event had smaller - * sequence number, so we will insert our packet after the event */ - if (event) - list = event; - - dts = item->dts; - if (item->rtptime == -1) - goto append; - - rtptime = item->rtptime; - /* rtp time jumps are checked for during skew calculation, but bypassed * in other mode, so mind those here and reset jb if needed. * Only reset if valid input time, which is likely for UDP input @@ -797,8 +721,7 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item, /* Return the last time if we got the same RTP timestamp again */ ext_rtptime = gst_rtp_buffer_ext_timestamp (&jbuf->ext_rtptime, rtptime); if (jbuf->last_rtptime != -1 && ext_rtptime == jbuf->last_rtptime) { - item->pts = jbuf->prev_out_time; - goto append; + return jbuf->prev_out_time; } /* keep track of the last extended rtptime */ @@ -906,12 +829,12 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item, external, rate_num, rate_denom); if (rtpsystime > base_time) - item->pts = rtpsystime - base_time; + pts = rtpsystime - base_time; else - item->pts = 0; + pts = 0; GST_DEBUG ("RFC7273 clock time %" GST_TIME_FORMAT ", out %" GST_TIME_FORMAT, - GST_TIME_ARGS (rtpsystime), GST_TIME_ARGS (item->pts)); + GST_TIME_ARGS (rtpsystime), GST_TIME_ARGS (pts)); } else if (rfc7273_mode && (jbuf->mode == RTP_JITTER_BUFFER_MODE_SLAVE || jbuf->mode == RTP_JITTER_BUFFER_MODE_SYNCED) && media_clock_offset != -1 && jbuf->rfc7273_sync) { @@ -960,12 +883,12 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item, /* All this assumes that the pipeline has enough additional * latency to cover for the network delay */ if (rtpsystime > base_time) - item->pts = rtpsystime - base_time; + pts = rtpsystime - base_time; else - item->pts = 0; + pts = 0; GST_DEBUG ("RFC7273 clock time %" GST_TIME_FORMAT ", out %" GST_TIME_FORMAT, - GST_TIME_ARGS (rtpsystime), GST_TIME_ARGS (item->pts)); + GST_TIME_ARGS (rtpsystime), GST_TIME_ARGS (pts)); } else { /* If we used the RFC7273 clock before and not anymore, * we need to resync it later again */ @@ -973,39 +896,40 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item, /* do skew calculation by measuring the difference between rtptime and the * receive dts, this function will return the skew corrected rtptime. */ - item->pts = calculate_skew (jbuf, ext_rtptime, gstrtptime, dts); + pts = calculate_skew (jbuf, ext_rtptime, gstrtptime, dts); } /* check if timestamps are not going backwards, we can only check this if we * have a previous out time and a previous send_diff */ - if (G_LIKELY (item->pts != -1 && jbuf->prev_out_time != -1 + if (G_LIKELY (pts != -1 && jbuf->prev_out_time != -1 && jbuf->prev_send_diff != -1)) { /* now check for backwards timestamps */ if (G_UNLIKELY ( /* if the server timestamps went up and the out_time backwards */ (gstrtptime - jbuf->base_rtptime > jbuf->prev_send_diff - && item->pts < jbuf->prev_out_time) || + && pts < jbuf->prev_out_time) || /* if the server timestamps went backwards and the out_time forwards */ (gstrtptime - jbuf->base_rtptime < jbuf->prev_send_diff - && item->pts > jbuf->prev_out_time) || + && pts > jbuf->prev_out_time) || /* if the server timestamps did not change */ gstrtptime - jbuf->base_rtptime == jbuf->prev_send_diff)) { GST_DEBUG ("backwards timestamps, using previous time"); - item->pts = jbuf->prev_out_time; + pts = jbuf->prev_out_time; } } - if (dts != -1 && item->pts + jbuf->delay < dts) { + + if (dts != -1 && pts + jbuf->delay < dts) { /* if we are going to produce a timestamp that is later than the input * timestamp, we need to reset the jitterbuffer. Likely the server paused * temporarily */ GST_DEBUG ("out %" GST_TIME_FORMAT " + %" G_GUINT64_FORMAT " < time %" - GST_TIME_FORMAT ", reset jitterbuffer", GST_TIME_ARGS (item->pts), + GST_TIME_FORMAT ", reset jitterbuffer", GST_TIME_ARGS (pts), jbuf->delay, GST_TIME_ARGS (dts)); rtp_jitter_buffer_resync (jbuf, dts, gstrtptime, ext_rtptime, TRUE); - item->pts = dts; + pts = dts; } - jbuf->prev_out_time = item->pts; + jbuf->prev_out_time = pts; jbuf->prev_send_diff = gstrtptime - jbuf->base_rtptime; if (media_clock) @@ -1013,6 +937,83 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item, if (pipeline_clock) gst_object_unref (pipeline_clock); + return pts; +} + + +/** + * rtp_jitter_buffer_insert: + * @jbuf: an #RTPJitterBuffer + * @item: an #RTPJitterBufferItem to insert + * @head: TRUE when the head element changed. + * @percent: the buffering percent after insertion + * + * Inserts @item into the packet queue of @jbuf. The sequence number of the + * packet will be used to sort the packets. This function takes ownerhip of + * @buf when the function returns %TRUE. + * + * When @head is %TRUE, the new packet was added at the head of the queue and + * will be available with the next call to rtp_jitter_buffer_pop() and + * rtp_jitter_buffer_peek(). + * + * Returns: %FALSE if a packet with the same number already existed. + */ +gboolean +rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item, + gboolean * head, gint * percent) +{ + GList *list, *event = NULL; + guint16 seqnum; + + g_return_val_if_fail (jbuf != NULL, FALSE); + g_return_val_if_fail (item != NULL, FALSE); + + list = jbuf->packets->tail; + + /* no seqnum, simply append then */ + if (item->seqnum == -1) + goto append; + + seqnum = item->seqnum; + + /* loop the list to skip strictly larger seqnum buffers */ + for (; list; list = g_list_previous (list)) { + guint16 qseq; + gint gap; + RTPJitterBufferItem *qitem = (RTPJitterBufferItem *) list; + + if (qitem->seqnum == -1) { + /* keep a pointer to the first consecutive event if not already + * set. we will insert the packet after the event if we can't find + * a packet with lower sequence number before the event. */ + if (event == NULL) + event = list; + continue; + } + + qseq = qitem->seqnum; + + /* compare the new seqnum to the one in the buffer */ + gap = gst_rtp_buffer_compare_seqnum (seqnum, qseq); + + /* we hit a packet with the same seqnum, notify a duplicate */ + if (G_UNLIKELY (gap == 0)) + goto duplicate; + + /* seqnum > qseq, we can stop looking */ + if (G_LIKELY (gap < 0)) + break; + + /* if we've found a packet with greater sequence number, cleanup the + * event pointer as the packet will be inserted before the event */ + event = NULL; + } + + /* if event is set it means that packets before the event had smaller + * sequence number, so we will insert our packet after the event */ + if (event) + list = event; + append: queue_do_insert (jbuf, list, (GList *) item); diff --git a/gst/rtpmanager/rtpjitterbuffer.h b/gst/rtpmanager/rtpjitterbuffer.h index 08ce169f91..d04b5fd96d 100644 --- a/gst/rtpmanager/rtpjitterbuffer.h +++ b/gst/rtpmanager/rtpjitterbuffer.h @@ -168,8 +168,7 @@ void rtp_jitter_buffer_reset_skew (RTPJitterBuffer *jbuf) gboolean rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf, RTPJitterBufferItem *item, - gboolean *head, gint *percent, - GstClockTime base_time); + gboolean *head, gint *percent); void rtp_jitter_buffer_disable_buffering (RTPJitterBuffer *jbuf, gboolean disabled); @@ -190,4 +189,7 @@ void rtp_jitter_buffer_get_sync (RTPJitterBuffer *jbuf, guint64 *timestamp, guint32 *clock_rate, guint64 *last_rtptime); +GstClockTime rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, + guint32 rtptime, GstClockTime base_time); + #endif /* __RTP_JITTER_BUFFER_H__ */ diff --git a/tests/check/elements/rtpjitterbuffer.c b/tests/check/elements/rtpjitterbuffer.c index 222aa3c92b..66b570cc6a 100644 --- a/tests/check/elements/rtpjitterbuffer.c +++ b/tests/check/elements/rtpjitterbuffer.c @@ -860,6 +860,148 @@ GST_START_TEST (test_num_late_when_considered_lost_arrives) GST_END_TEST; +GST_START_TEST (test_lost_event_uses_pts) +{ + GstHarness *h = gst_harness_new ("rtpjitterbuffer"); + GstEvent *out_event; + GstClockTime now; + gint jb_latency_ms = 100; + gint i; + + gst_harness_set_src_caps (h, generate_caps ()); + g_object_set (h->element, "do-lost", TRUE, "latency", jb_latency_ms, NULL); + + /* push the first buffer through */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (0))); + fail_unless (gst_harness_crank_single_clock_wait (h)); + gst_buffer_unref (gst_harness_pull (h)); + + /* push some buffers arriving in perfect time! */ + for (i = 1; i <= 2; i++) { + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (i))); + gst_buffer_unref (gst_harness_pull (h)); + } + + /* hop over 1 packets (seqnum 3) and make another one (gap of 1), but due to + network delays, this packets is also grossly late */ + i = 4; + + /* advance the clock to the latest possible time buffer 4 could arrive */ + now = i * PCMU_BUF_DURATION + jb_latency_ms * GST_MSECOND; + gst_harness_set_time (h, now); + gst_harness_push (h, generate_test_buffer_full (now, FALSE, i, i * PCMU_RTP_TS_DURATION)); + + /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */ + for (int i = 0; i < 3; i++) + gst_event_unref (gst_harness_pull_event (h)); + + /* we should now have received a packet-lost-event for buffer 3 */ + out_event = gst_harness_pull_event (h); + verify_lost_event (out_event, 3, 3 * PCMU_BUF_DURATION, PCMU_BUF_DURATION); + + /* and pull out packet 4 */ + gst_buffer_unref (gst_harness_pull (h)); + + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 4, + "num-lost", G_TYPE_UINT64, (guint64) 1, NULL))); + + gst_harness_teardown (h); +} + +GST_END_TEST; + +GST_START_TEST (test_lost_event_with_backwards_rtptime) +{ + GstHarness *h = gst_harness_new ("rtpjitterbuffer"); + GstEvent *out_event; + gint jb_latency_ms = 100; + gint i; + + gst_harness_set_src_caps (h, generate_caps ()); + g_object_set (h->element, "do-lost", TRUE, "latency", jb_latency_ms, NULL); + + /* push the first buffer through */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (0))); + fail_unless (gst_harness_crank_single_clock_wait (h)); + gst_buffer_unref (gst_harness_pull (h)); + + /* push some buffers arriving in perfect time! */ + for (i = 1; i <= 2; i++) { + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (i))); + gst_buffer_unref (gst_harness_pull (h)); + } + + /* + For video using B-frames, an expected sequence + could be like this: + (I = I-frame, P = P-frame, B = B-frame) + ___ ___ ___ ___ ___ + ... | 3 | | 4 | | 5 | | 6 | | 7 | + ––– ––– ––– ––– ––– + rtptime: 3(I) 5(P) 5(P) 4(B) 6(P) +arrival(dts): 3 5 5 5 6 + + Notice here that packet 6 (the B frame) make + the rtptime go backwards. + + But we get this: + ___ ___ _ _ ___ ___ + ... | 3 | | 4 | | | | 6 | | 7 | + ––– ––– - - ––– ––– + rtptime: 3(I) 5(P) 4(B) 6(P) +arrival(dts): 3 5 5 6 + + */ + + /* seqnum 3 */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (3))); + gst_buffer_unref (gst_harness_pull (h)); + + /* seqnum 4, arriving at time 5 with rtptime 5 */ + gst_harness_push (h, generate_test_buffer_full ( + 5 * PCMU_BUF_DURATION, FALSE, 4, 5 * PCMU_RTP_TS_DURATION)); + gst_buffer_unref (gst_harness_pull (h)); + + /* seqnum 6, arriving at time 5 with rtptime 4, + making a gap for missing seqnum 5 */ + gst_harness_push (h, generate_test_buffer_full ( + 5 * PCMU_BUF_DURATION, FALSE, 6, 4 * PCMU_RTP_TS_DURATION)); + + /* seqnum 7, arriving at time 6 with rtptime 6 */ + gst_harness_push (h, generate_test_buffer_full ( + 6 * PCMU_BUF_DURATION, FALSE, 7, 6 * PCMU_RTP_TS_DURATION)); + + /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */ + for (int i = 0; i < 3; i++) + gst_event_unref (gst_harness_pull_event (h)); + + /* we should now have received a packet-lost-event for seqnum 5, + with time 5 and 0 duration */ + gst_harness_crank_single_clock_wait (h); + out_event = gst_harness_pull_event (h); + verify_lost_event (out_event, 5, 5 * PCMU_BUF_DURATION, 0); + + /* and pull out 6 and 7 */ + gst_buffer_unref (gst_harness_pull (h)); + gst_buffer_unref (gst_harness_pull (h)); + + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 7, + "num-lost", G_TYPE_UINT64, (guint64) 1, NULL))); + + gst_harness_teardown (h); +} + +GST_END_TEST; + GST_START_TEST (test_all_packets_are_timestamped_zero) { GstHarness *h = gst_harness_new ("rtpjitterbuffer"); @@ -1228,110 +1370,6 @@ GST_START_TEST (test_rtx_two_missing) GST_END_TEST; -GST_START_TEST (text_rtx_two_missing_early) -{ - GstHarness *h = gst_harness_new ("rtpjitterbuffer"); - GstTestClock *testclock; - gint latency_ms = 30; - GstClockTime last_rtx_request, now; - - testclock = gst_harness_get_testclock (h); - gst_harness_set_src_caps (h, generate_caps ()); - g_object_set (h->element, "do-retransmission", TRUE, "latency", latency_ms, - NULL); - - for (gint i = 0; i <= latency_ms / PCMU_BUF_MS; i++) { - gst_test_clock_set_time (testclock, i * PCMU_BUF_DURATION); - fail_unless_equals_int (GST_FLOW_OK, - gst_harness_push (h, generate_test_buffer (i))); - gst_harness_wait_for_clock_id_waits (h, 1, 60); - } - - gst_harness_crank_single_clock_wait (h); - fail_unless_equals_int64 (latency_ms * GST_MSECOND, - gst_clock_get_time (GST_CLOCK (testclock))); - - for (gint i = 0; i <= latency_ms / PCMU_BUF_MS; i++) - gst_buffer_unref (gst_harness_pull (h)); - - /* drop reconfigure event */ - gst_event_unref (gst_harness_pull_upstream_event (h)); - - /* - The expected sequence of buffers is this: - ___ ___ ___ ___ ___ - | 0 | | 1 | | 2 | | 3 | | 4 | - ––– ––– ––– ––– ––– - 0ms 20ms 40ms 60ms 80ms - - But instead we get this: - ___ ___ _ _ _ _ ___ - | 0 | | 1 | | | | | | 4 | - ––– ––– – – – – ––– - 0ms 20ms 41ms - - */ - - now = 41 * GST_MSECOND; - gst_harness_set_time (h, now); - fail_unless_equals_int (GST_FLOW_OK, - gst_harness_push (h, - generate_test_buffer_full (now, TRUE, 4, 4 * PCMU_RTP_TS_DURATION))); - - /* - - With the now calculated packet-spacing of (41-20) / 3 = 7, - giving us these expected times: - ___ ___ ___ ___ ___ - | 0 | | 1 | | 2 | | 3 | | 4 | - ––– ––– ––– ––– ––– - 0ms 20ms 27ms 34ms 41ms - - For RTX, the inital RTX-timeouts for the missing buffers are - the expected arrival time + half the packet-spacing time, like this: - ___ ___ - | 2 | | 3 | - ––– ––– - 50ms 70ms - - But since we have re-calculated the estimated arrival-time - of these buffers, we have to adjust the RTX timeout as well, - and we use the original delay (packet-spacing / 2) = 10ms, - and add it on: - ___ ___ - | 2 | | 3 | - ––– ––– - 37ms 44ms - - Also note that the first RTX request is now scheduled for a - time that is prior to NOW (37ms < 41ms), so it will be sent straight - away without us needing to "crank" the timer-thread - - */ - - /* The RTX request for packet 2 has timestamp 27ms and delay 10ms */ - verify_rtx_event (gst_harness_pull_upstream_event (h), - 2, 27 * GST_MSECOND, 10, PCMU_BUF_DURATION); - /* and is sent immediately after packet 4 arrives (41ms) */ - last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock)); - fail_unless_equals_int64 (last_rtx_request, now); - - /* crank the timer thread */ - gst_harness_crank_single_clock_wait (h); - - /* The RTX request for packet 3 has timestamp 34ms and delay 10ms */ - verify_rtx_event (gst_harness_pull_upstream_event (h), - 3, 34 * GST_MSECOND, 10, PCMU_BUF_DURATION); - /* and is sent at 44ms */ - last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock)); - fail_unless_equals_int64 (last_rtx_request, 44 * GST_MSECOND); - - gst_object_unref (testclock); - gst_harness_teardown (h); -} - -GST_END_TEST; - GST_START_TEST (test_rtx_packet_delay) { GstHarness *h = gst_harness_new ("rtpjitterbuffer"); @@ -1365,9 +1403,10 @@ GST_START_TEST (test_rtx_packet_delay) * the estimate for 2 could be refined now to 20ms. also packet 2, 3 and 4 * are exceeding the max allowed reorder distance and should request a * retransmission right away */ + gst_harness_set_time (h, 1 * PCMU_BUF_DURATION); fail_unless_equals_int (GST_FLOW_OK, - gst_harness_push (h, generate_test_buffer_full (20 * GST_MSECOND, TRUE, 8, - 8 * PCMU_RTP_TS_DURATION))); + gst_harness_push (h, generate_test_buffer_full (1 * PCMU_BUF_DURATION, TRUE, 8, + 1 * PCMU_RTP_TS_DURATION))); /* drop reconfigure event */ gst_event_unref (gst_harness_pull_upstream_event (h)); @@ -1377,23 +1416,23 @@ GST_START_TEST (test_rtx_packet_delay) /* we should now receive retransmission requests for 2 -> 5 */ out_event = gst_harness_pull_upstream_event (h); - verify_rtx_event (out_event, 2, 20 * GST_MSECOND, 17, PCMU_BUF_DURATION); + verify_rtx_event (out_event, 2, 20 * GST_MSECOND, 10, PCMU_BUF_DURATION); for (i = 3; i < 5; i++) { GST_DEBUG ("popping %d", i); out_event = gst_harness_pull_upstream_event (h); - verify_rtx_event (out_event, i, 20 * GST_MSECOND, 17, PCMU_BUF_DURATION); + verify_rtx_event (out_event, i, 20 * GST_MSECOND, 10, PCMU_BUF_DURATION); } fail_unless_equals_int (0, gst_harness_upstream_events_in_queue (h)); /* push 9, this should immediately request retransmission of 5 */ fail_unless_equals_int (GST_FLOW_OK, - gst_harness_push (h, generate_test_buffer_full (20 * GST_MSECOND, TRUE, 9, - 9 * PCMU_RTP_TS_DURATION))); + gst_harness_push (h, generate_test_buffer_full (1 * PCMU_BUF_DURATION, TRUE, 9, + 1 * PCMU_RTP_TS_DURATION))); /* we should now receive retransmission requests for 5 */ out_event = gst_harness_pull_upstream_event (h); - verify_rtx_event (out_event, 5, 20 * GST_MSECOND, 17, PCMU_BUF_DURATION); + verify_rtx_event (out_event, 5, 20 * GST_MSECOND, 10, PCMU_BUF_DURATION); /* wait for timeout for rtx 6 -> 7 */ gst_test_clock_set_time_and_process (testclock, 60 * GST_MSECOND); @@ -1401,7 +1440,7 @@ GST_START_TEST (test_rtx_packet_delay) for (i = 6; i < 8; i++) { GST_DEBUG ("popping %d", i); out_event = gst_harness_pull_upstream_event (h); - verify_rtx_event (out_event, i, 20 * GST_MSECOND, 17, PCMU_BUF_DURATION); + verify_rtx_event (out_event, i, 20 * GST_MSECOND, 10, PCMU_BUF_DURATION); } /* churn through 7 sync_times until the new buffer gets pushed out */ @@ -2137,6 +2176,83 @@ GST_START_TEST (test_rtx_same_delay_and_retry_timeout) GST_END_TEST; +GST_START_TEST (test_rtx_with_backwards_rtptime) +{ + GstHarness *h = gst_harness_new ("rtpjitterbuffer"); + GstEvent *event; + gint jb_latency_ms = 40; + gint i; + + gst_harness_set_src_caps (h, generate_caps ()); + g_object_set (h->element, + "do-retransmission", TRUE, + "latency", jb_latency_ms, + NULL); + + /* push the first buffer through */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (0))); + fail_unless (gst_harness_crank_single_clock_wait (h)); + gst_buffer_unref (gst_harness_pull (h)); + + /* push some buffers arriving in perfect time! */ + for (i = 1; i <= 2; i++) { + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (i))); + gst_buffer_unref (gst_harness_pull (h)); + } + + /* + For video using B-frames, an expected sequence + could be like this: + (I = I-frame, P = P-frame, B = B-frame) + ___ ___ ___ + ... | 3 | | 4 | | 5 | + ––– ––– ––– + rtptime: 3(I) 5(P) 4(B) +arrival(dts): 3 5 5 + + Notice here that packet 5 (the B frame) make + the rtptime go backwards. + */ + + /* seqnum 3, arriving at time 3 with rtptime 3 */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (3))); + gst_buffer_unref (gst_harness_pull (h)); + + /* seqnum 4, arriving at time 5 with rtptime 5 */ + gst_harness_push (h, generate_test_buffer_full ( + 5 * PCMU_BUF_DURATION, FALSE, 4, 5 * PCMU_RTP_TS_DURATION)); + gst_buffer_unref (gst_harness_pull (h)); + + /* seqnum 5, arriving at time 5 with rtptime 4 */ + gst_harness_push (h, generate_test_buffer_full ( + 5 * PCMU_BUF_DURATION, FALSE, 5, 4 * PCMU_RTP_TS_DURATION)); + + /* drop reconfigure event */ + gst_event_unref (gst_harness_pull_upstream_event (h)); + + /* crank to time-out the rtx-request for seqnum 6, the point here + being that the backwards rtptime did not mess up the timeout for + the rtx event */ + gst_harness_crank_single_clock_wait (h); + event = gst_harness_pull_upstream_event (h); + verify_rtx_event (event, 6, 5 * PCMU_BUF_DURATION + 15 * GST_MSECOND, + 17, 35 * GST_MSECOND); + + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 6, + "rtx-count", G_TYPE_UINT64, (guint64) 1, + "num-lost", G_TYPE_UINT64, (guint64) 0, NULL))); + + gst_harness_teardown (h); +} + +GST_END_TEST; + + GST_START_TEST (test_gap_exceeds_latency) { GstHarness *h = gst_harness_new ("rtpjitterbuffer"); @@ -2325,13 +2441,12 @@ GST_START_TEST (test_deadline_ts_offset) GST_END_TEST; -GST_START_TEST (test_dts_gap_larger_than_latency) +GST_START_TEST (test_gap_larger_than_latency) { GstHarness *h = gst_harness_new ("rtpjitterbuffer"); GstTestClock *testclock; GstEvent *out_event; gint jb_latency_ms = 100; - GstClockTime dts_after_gap = (jb_latency_ms + 1) * GST_MSECOND; gst_harness_set_src_caps (h, generate_caps ()); testclock = gst_harness_get_testclock (h); @@ -2343,26 +2458,34 @@ GST_START_TEST (test_dts_gap_larger_than_latency) fail_unless (gst_harness_crank_single_clock_wait (h)); gst_buffer_unref (gst_harness_pull (h)); - /* Push packet with DTS larger than latency */ + /* we have 100ms latency, so drop 6 packets (120ms) */ + gst_harness_set_time (h, 7 * PCMU_BUF_DURATION); fail_unless_equals_int (GST_FLOW_OK, - gst_harness_push (h, generate_test_buffer_full (dts_after_gap, - TRUE, 5, 5 * PCMU_RTP_TS_DURATION))); + gst_harness_push (h, generate_test_buffer (7))); /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */ for (int i = 0; i < 3; i++) gst_event_unref (gst_harness_pull_event (h)); - /* Time out and verify lost events */ - for (gint i = 1; i < 5; i++) { - GstClockTime dur = dts_after_gap / 5; + /* Packet 1 and 2 are already lost at this point */ + for (gint i = 1; i <= 2; i++) { + out_event = gst_harness_pull_event (h); + verify_lost_event (out_event, i, i * PCMU_BUF_DURATION, PCMU_BUF_DURATION); + } + + /* Packets 3-6 have to be timed out */ + for (gint i = 3; i <= 6; i++) { fail_unless (gst_harness_crank_single_clock_wait (h)); out_event = gst_harness_pull_event (h); - verify_lost_event (out_event, i, i * dur, dur); + verify_lost_event (out_event, i, i * PCMU_BUF_DURATION, PCMU_BUF_DURATION); } + /* and finally pull out buffer 7 */ + gst_buffer_unref (gst_harness_pull (h)); + fail_unless (verify_jb_stats (h->element, gst_structure_new ("application/x-rtp-jitterbuffer-stats", - "num-lost", G_TYPE_UINT64, (guint64) 4, NULL))); + "num-lost", G_TYPE_UINT64, (guint64) 6, NULL))); gst_object_unref (testclock); gst_harness_teardown (h); @@ -2555,6 +2678,9 @@ rtpjitterbuffer_suite (void) tcase_add_test (tc_chain, test_only_one_lost_event_on_large_gaps); tcase_add_test (tc_chain, test_two_lost_one_arrives_in_time); tcase_add_test (tc_chain, test_late_packets_still_makes_lost_events); + tcase_add_test (tc_chain, test_lost_event_uses_pts); + tcase_add_test (tc_chain, test_lost_event_with_backwards_rtptime); + tcase_add_test (tc_chain, test_all_packets_are_timestamped_zero); tcase_add_loop_test (tc_chain, test_num_late_when_considered_lost_arrives, 0, 2); @@ -2564,7 +2690,6 @@ rtpjitterbuffer_suite (void) tcase_add_test (tc_chain, test_rtx_expected_next); tcase_add_test (tc_chain, test_rtx_two_missing); - tcase_add_test (tc_chain, text_rtx_two_missing_early); tcase_add_test (tc_chain, test_rtx_packet_delay); tcase_add_test (tc_chain, test_rtx_buffer_arrives_just_in_time); tcase_add_test (tc_chain, test_rtx_buffer_arrives_too_late); @@ -2575,10 +2700,11 @@ rtpjitterbuffer_suite (void) tcase_add_test (tc_chain, test_rtx_rtt_larger_than_retry_timeout); tcase_add_test (tc_chain, test_rtx_no_request_if_time_past_retry_period); tcase_add_test (tc_chain, test_rtx_same_delay_and_retry_timeout); + tcase_add_test (tc_chain, test_rtx_with_backwards_rtptime); tcase_add_test (tc_chain, test_gap_exceeds_latency); tcase_add_test (tc_chain, test_deadline_ts_offset); - tcase_add_test (tc_chain, test_dts_gap_larger_than_latency); + tcase_add_test (tc_chain, test_gap_larger_than_latency); tcase_add_test (tc_chain, test_push_big_gap); tcase_add_loop_test (tc_chain,