1. 简介:
本文主要描述gstreamer中rtpjitterbuffer的定时器线程的处理流程,定时器主要对丢包进行延迟处理。
2. 流程:
2.1 定时器线程主要流程:
1) 当rtpjitterbuffer组件状态从READY升至PAUSED时,会创建出定时器的子线程。
2) 从当前定时器中找到超时时间最早的定时器。
3) 如果没有找到定时器,说明当前并没有定时器被添加,此时会挂起本线程,等待主线程上丢包后添加相应的定时器。
4) 找到定时器后,与当前时间比较,如果已经超时,则处理超时事件,处理超时事件完成后回到4)。
5) 如果这个定时器还没有超时,说明所有定时器都还没有到,本线程进入睡眠,待睡眠醒来时,回到2)。
流程如图:
2.2 超时重传处理流程:
这里主要分析单个包丢失后的处理流程。
1) 当一个不是预期的包到来,且序号大于预期包,则认为预期包可能丢包,这时候进入丢包处理流程。
2) 首先通过函数calculate_expected计算第一次超时的时间,这里使用的是预期包和实际到达包的序号差和dts的比值,线性增大的时间戳作为超时时间,具体见下分析。
3) 为对应包序添加定时器,此时非预期的包照常处理,依旧进入jbuf,但是由于包序不连续,推送线程不会把数据往下游发送。
4) 定时器线程等待超时时间的到来。
5) 如果超时到来之前,预期包到达,则删除定时器,进入正常的包处理流程,定时器线程唤醒后没有对应的定时器事件,则进行其他处理。
6) 如果超时到来之前,包都没有到达,则定时器触发,需要判断当前重试次数是否已经达到上限,或者重试时间超过限制的总时间。
7) 如果尝试满了,则将包设置为LOST,在下一个超时后执行do_lost_timeout操作,将该序号的包置位LOST的event向下游发送。
8) 如果还可以继续尝试,则刷新当前尝试次数和尝试时间,重新调度定时器,再次加入调度。
流程如图:
3. 代码分析:
3.1 主入口函数wait_next_timeout
当rtpjitterbuffer从READY状态转换到PAUSED状态时,会创建一个子线程用来对所有的定时器事件进行管理。
其代码如下,虽然比较冗长,但是处理流程比较简单,如上描述。
/* called when we need to wait for the next timeout.
*
* We loop over the array of recorded timeouts and wait for the earliest one.
* When it timed out, do the logic associated with the timer.
*
* If there are no timers, we wait on a gcond until something new happens.
*/
static void
wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
{
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
GstClockTime now = 0;
JBUF_LOCK (priv);
while (priv->timer_running) {
TimerData *timer = NULL;
GstClockTime timer_timeout = -1;
gint i, len;
/* If we have a clock, update "now" now with the very
* latest running time we have. If timers are unscheduled below we
* otherwise wouldn't update now (it's only updated when timers
* expire), and also for the very first loop iteration now would
* otherwise always be 0
*/
// 获取当前时间,主要用来和定时器的超时时间做对比。
GST_OBJECT_LOCK (jitterbuffer);
if (GST_ELEMENT_CLOCK (jitterbuffer)) {
now =
gst_clock_get_time (GST_ELEMENT_CLOCK (jitterbuffer)) -
GST_ELEMENT_CAST (jitterbuffer)->base_time;
}
GST_OBJECT_UNLOCK (jitterbuffer);
GST_DEBUG_OBJECT (jitterbuffer, "now %" GST_TIME_FORMAT,
GST_TIME_ARGS (now));
len = priv->timers->len;
// 遍历所有定时器,找到其中时间最小的。
for (i = 0; i < len; i++) {
TimerData *test = &g_array_index (priv->timers, TimerData, i);
GstClockTime test_timeout = get_timeout (jitterbuffer, test);
gboolean save_best = FALSE;
GST_DEBUG_OBJECT (jitterbuffer, "%d, %d, %d, %" GST_TIME_FORMAT,
i, test->type, test->seqnum, GST_TIME_ARGS (test_timeout));
/* find the smallest timeout */
if (timer == NULL) {
// 当前没有最小值
save_best = TRUE;
} else if (timer_timeout == -1) {
// 不是很确定什么场景会有超时时间为-1的,因此这两个分支不分析。
/* we already have an immediate timeout, the new timer must be an
* immediate timer with smaller seqnum to become the best */
if (te