static void rtp_jitter_buffer_init (RTPJitterBuffer * jbuf) { jbuf->packets = g_queue_new (); rtp_jitter_buffer_reset_skew (jbuf); }
static void rtp_jitter_buffer_init (RTPJitterBuffer * jbuf) { jbuf->packets = g_queue_new (); jbuf->mode = RTP_JITTER_BUFFER_MODE_SLAVE; rtp_jitter_buffer_reset_skew (jbuf); }
/** * rtp_jitter_buffer_set_clock_rate: * @jbuf: an #RTPJitterBuffer * * Set the clock rate in the jitterbuffer. */ void rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer * jbuf, guint32 clock_rate) { if (jbuf->clock_rate != clock_rate) { if (jbuf->clock_rate == -1) { GST_DEBUG ("Clock rate changed from %" G_GUINT32_FORMAT " to %" G_GUINT32_FORMAT, jbuf->clock_rate, clock_rate); } else { GST_WARNING ("Clock rate changed from %" G_GUINT32_FORMAT " to %" G_GUINT32_FORMAT, jbuf->clock_rate, clock_rate); } jbuf->clock_rate = clock_rate; rtp_jitter_buffer_reset_skew (jbuf); } }
static void gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer) { GstRtpJitterBufferPrivate *priv; priv = jitterbuffer->priv; JBUF_LOCK (priv); GST_DEBUG_OBJECT (jitterbuffer, "Enabling pop on queue"); /* Mark as non flushing */ priv->srcresult = GST_FLOW_OK; gst_segment_init (&priv->segment, GST_FORMAT_TIME); priv->last_popped_seqnum = -1; priv->last_out_time = -1; priv->next_seqnum = -1; priv->clock_rate = -1; priv->eos = FALSE; rtp_jitter_buffer_flush (priv->jbuf); rtp_jitter_buffer_reset_skew (priv->jbuf); JBUF_UNLOCK (priv); }
/** * rtp_jitter_buffer_insert: * @jbuf: an #RTPJitterBuffer * @item: an #RTPJitterBufferItem to insert * @tail: TRUE when the tail 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. * * Returns: %FALSE if a packet with the same number already existed. */ gboolean rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item, gboolean * tail, gint * percent) { GList *list = NULL; guint32 rtptime; guint16 seqnum; GstClockTime dts; g_return_val_if_fail (jbuf != NULL, FALSE); g_return_val_if_fail (item != NULL, FALSE); /* no seqnum, simply append then */ if (item->seqnum == -1) { goto append; } seqnum = item->seqnum; /* loop the list to skip strictly smaller seqnum buffers */ for (list = jbuf->packets->head; list; list = g_list_next (list)) { guint16 qseq; gint gap; RTPJitterBufferItem *qitem = (RTPJitterBufferItem *) list; if (qitem->seqnum == -1) 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; } 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 * where we expect this might happen due to async thread effects * (in seek and state change cycles), but not so much for TCP input */ if (GST_CLOCK_TIME_IS_VALID (dts) && jbuf->mode != RTP_JITTER_BUFFER_MODE_SLAVE && jbuf->base_time != -1 && jbuf->last_rtptime != -1) { GstClockTime ext_rtptime = jbuf->ext_rtptime; ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime); if (ext_rtptime > jbuf->last_rtptime + 3 * jbuf->clock_rate || ext_rtptime + 3 * jbuf->clock_rate < jbuf->last_rtptime) { /* reset even if we don't have valid incoming time; * still better than producing possibly very bogus output timestamp */ GST_WARNING ("rtp delta too big, reset skew"); rtp_jitter_buffer_reset_skew (jbuf); } } switch (jbuf->mode) { case RTP_JITTER_BUFFER_MODE_NONE: case RTP_JITTER_BUFFER_MODE_BUFFER: /* send 0 as the first timestamp and -1 for the other ones. This will * interpollate them from the RTP timestamps with a 0 origin. In buffering * mode we will adjust the outgoing timestamps according to the amount of * time we spent buffering. */ if (jbuf->base_time == -1) dts = 0; else dts = -1; break; case RTP_JITTER_BUFFER_MODE_SYNCED: /* synchronized clocks, take first timestamp as base, use RTP timestamps * to interpolate */ if (jbuf->base_time != -1) dts = -1; break; case RTP_JITTER_BUFFER_MODE_SLAVE: default: break; } /* 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, rtptime, dts); append: queue_do_insert (jbuf, list, (GList *) item); /* buffering mode, update buffer stats */ if (jbuf->mode == RTP_JITTER_BUFFER_MODE_BUFFER) update_buffer_level (jbuf, percent); else if (percent) *percent = -1; /* tail was changed when we did not find a previous packet, we set the return * flag when requested. */ if (G_LIKELY (tail)) *tail = (list == NULL); return TRUE; /* ERRORS */ duplicate: { GST_WARNING ("duplicate packet %d found", (gint) seqnum); return FALSE; } }
static GstStateChangeReturn gst_rtp_jitter_buffer_change_state (GstElement * element, GstStateChange transition) { GstRtpJitterBuffer *jitterbuffer; GstRtpJitterBufferPrivate *priv; GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; jitterbuffer = GST_RTP_JITTER_BUFFER (element); priv = jitterbuffer->priv; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: JBUF_LOCK (priv); /* reset negotiated values */ priv->clock_rate = -1; priv->clock_base = -1; priv->peer_latency = 0; priv->last_pt = -1; /* block until we go to PLAYING */ priv->blocked = TRUE; /* reset skew detection initialy */ rtp_jitter_buffer_reset_skew (priv->jbuf); JBUF_UNLOCK (priv); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: JBUF_LOCK (priv); /* unblock to allow streaming in PLAYING */ priv->blocked = FALSE; JBUF_SIGNAL (priv); JBUF_UNLOCK (priv); break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: /* we are a live element because we sync to the clock, which we can only * do in the PLAYING state */ if (ret != GST_STATE_CHANGE_FAILURE) ret = GST_STATE_CHANGE_NO_PREROLL; break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: JBUF_LOCK (priv); /* block to stop streaming when PAUSED */ priv->blocked = TRUE; JBUF_UNLOCK (priv); if (ret != GST_STATE_CHANGE_FAILURE) ret = GST_STATE_CHANGE_NO_PREROLL; break; case GST_STATE_CHANGE_PAUSED_TO_READY: break; case GST_STATE_CHANGE_READY_TO_NULL: break; default: break; } return ret; }
/** * rtp_jitter_buffer_insert: * @jbuf: an #RTPJitterBuffer * @buf: a buffer * @time: a running_time when this buffer was received in nanoseconds * @clock_rate: the clock-rate of the payload of @buf * @max_delay: the maximum lateness of @buf * @tail: TRUE when the tail element changed. * * Inserts @buf 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. * @buf should have writable metadata when calling this function. * * Returns: %FALSE if a packet with the same number already existed. */ gboolean rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf, GstClockTime time, guint32 clock_rate, gboolean * tail, gint * percent) { GList *list; guint32 rtptime; guint16 seqnum; GstRTPBuffer rtp = {NULL}; g_return_val_if_fail (jbuf != NULL, FALSE); g_return_val_if_fail (buf != NULL, FALSE); gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); seqnum = gst_rtp_buffer_get_seq (&rtp); /* loop the list to skip strictly smaller seqnum buffers */ for (list = jbuf->packets->head; list; list = g_list_next (list)) { guint16 qseq; gint gap; GstRTPBuffer rtpb = {NULL}; gst_rtp_buffer_map (GST_BUFFER_CAST (list->data), GST_MAP_READ, &rtpb); qseq = gst_rtp_buffer_get_seq (&rtpb); gst_rtp_buffer_unmap (&rtpb); /* 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; } rtptime = gst_rtp_buffer_get_timestamp (&rtp); /* 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 * where we expect this might happen due to async thread effects * (in seek and state change cycles), but not so much for TCP input */ if (GST_CLOCK_TIME_IS_VALID (time) && jbuf->mode != RTP_JITTER_BUFFER_MODE_SLAVE && jbuf->base_time != -1 && jbuf->last_rtptime != -1) { GstClockTime ext_rtptime = jbuf->ext_rtptime; ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime); if (ext_rtptime > jbuf->last_rtptime + 3 * clock_rate || ext_rtptime + 3 * clock_rate < jbuf->last_rtptime) { /* reset even if we don't have valid incoming time; * still better than producing possibly very bogus output timestamp */ GST_WARNING ("rtp delta too big, reset skew"); rtp_jitter_buffer_reset_skew (jbuf); } } switch (jbuf->mode) { case RTP_JITTER_BUFFER_MODE_NONE: case RTP_JITTER_BUFFER_MODE_BUFFER: /* send 0 as the first timestamp and -1 for the other ones. This will * interpollate them from the RTP timestamps with a 0 origin. In buffering * mode we will adjust the outgoing timestamps according to the amount of * time we spent buffering. */ if (jbuf->base_time == -1) time = 0; else time = -1; break; case RTP_JITTER_BUFFER_MODE_SLAVE: default: break; } /* do skew calculation by measuring the difference between rtptime and the * receive time, this function will retimestamp @buf with the skew corrected * running time. */ time = calculate_skew (jbuf, rtptime, time, clock_rate); GST_BUFFER_TIMESTAMP (buf) = time; /* It's more likely that the packet was inserted in the front of the buffer */ if (G_LIKELY (list)) g_queue_insert_before (jbuf->packets, list, buf); else g_queue_push_tail (jbuf->packets, buf); /* buffering mode, update buffer stats */ if (jbuf->mode == RTP_JITTER_BUFFER_MODE_BUFFER) update_buffer_level (jbuf, percent); else *percent = -1; /* tail was changed when we did not find a previous packet, we set the return * flag when requested. */ if (G_LIKELY (tail)) *tail = (list == NULL); gst_rtp_buffer_unmap (&rtp); return TRUE; /* ERRORS */ duplicate: { gst_rtp_buffer_unmap (&rtp); GST_WARNING ("duplicate packet %d found", (gint) seqnum); return FALSE; } }