• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer RTP Manager
2  *
3  * Copyright (C) 2019 Net Insight AB
4  *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 #include <string.h>
22 
23 #include <gst/rtp/gstrtpbuffer.h>
24 
25 #include "rtptimerqueue.h"
26 
27 struct _RtpTimerQueue
28 {
29   GObject parent;
30 
31   GQueue timers;
32   GHashTable *hashtable;
33 };
34 
35 G_DEFINE_TYPE (RtpTimerQueue, rtp_timer_queue, G_TYPE_OBJECT);
36 
37 /* some timer private helpers */
38 
39 static RtpTimer *
rtp_timer_new(void)40 rtp_timer_new (void)
41 {
42   return g_slice_new0 (RtpTimer);
43 }
44 
45 static inline void
rtp_timer_set_next(RtpTimer * timer,RtpTimer * next)46 rtp_timer_set_next (RtpTimer * timer, RtpTimer * next)
47 {
48   GList *list = (GList *) timer;
49   list->next = (GList *) next;
50 }
51 
52 static inline void
rtp_timer_set_prev(RtpTimer * timer,RtpTimer * prev)53 rtp_timer_set_prev (RtpTimer * timer, RtpTimer * prev)
54 {
55   GList *list = (GList *) timer;
56   list->prev = (GList *) prev;
57 }
58 
59 static inline gboolean
rtp_timer_is_later(RtpTimer * timer,RtpTimer * next)60 rtp_timer_is_later (RtpTimer * timer, RtpTimer * next)
61 {
62   if (next == NULL)
63     return FALSE;
64 
65   if (GST_CLOCK_TIME_IS_VALID (next->timeout)) {
66     if (!GST_CLOCK_TIME_IS_VALID (timer->timeout))
67       return FALSE;
68 
69     if (timer->timeout > next->timeout)
70       return TRUE;
71   }
72 
73   if (timer->timeout == next->timeout &&
74       gst_rtp_buffer_compare_seqnum (timer->seqnum, next->seqnum) < 0)
75     return TRUE;
76 
77   return FALSE;
78 }
79 
80 static inline gboolean
rtp_timer_is_sooner(RtpTimer * timer,RtpTimer * prev)81 rtp_timer_is_sooner (RtpTimer * timer, RtpTimer * prev)
82 {
83   if (prev == NULL)
84     return FALSE;
85 
86   if (GST_CLOCK_TIME_IS_VALID (prev->timeout)) {
87     if (!GST_CLOCK_TIME_IS_VALID (timer->timeout))
88       return TRUE;
89 
90     if (timer->timeout < prev->timeout)
91       return TRUE;
92   }
93 
94   if (timer->timeout == prev->timeout &&
95       gst_rtp_buffer_compare_seqnum (timer->seqnum, prev->seqnum) > 0)
96     return TRUE;
97 
98   return FALSE;
99 }
100 
101 static inline gboolean
rtp_timer_is_closer_to_head(RtpTimer * timer,RtpTimer * head)102 rtp_timer_is_closer_to_head (RtpTimer * timer, RtpTimer * head)
103 {
104   RtpTimer *prev = rtp_timer_get_prev (timer);
105   GstClockTimeDiff prev_delta = 0;
106   GstClockTimeDiff head_delta = 0;
107 
108   if (prev == NULL)
109     return FALSE;
110 
111   if (rtp_timer_is_sooner (timer, head))
112     return TRUE;
113 
114   if (rtp_timer_is_later (timer, prev))
115     return FALSE;
116 
117   if (prev->timeout == head->timeout) {
118     gint prev_gap, head_gap;
119 
120     prev_gap = gst_rtp_buffer_compare_seqnum (timer->seqnum, prev->seqnum);
121     head_gap = gst_rtp_buffer_compare_seqnum (head->seqnum, timer->seqnum);
122 
123     if (head_gap < prev_gap)
124       return TRUE;
125   }
126 
127   if (GST_CLOCK_TIME_IS_VALID (timer->timeout) &&
128       GST_CLOCK_TIME_IS_VALID (head->timeout)) {
129     prev_delta = GST_CLOCK_DIFF (timer->timeout, prev->timeout);
130     head_delta = GST_CLOCK_DIFF (head->timeout, timer->timeout);
131 
132     if (head_delta < prev_delta)
133       return TRUE;
134   }
135 
136   return FALSE;
137 }
138 
139 static inline gboolean
rtp_timer_is_closer_to_tail(RtpTimer * timer,RtpTimer * tail)140 rtp_timer_is_closer_to_tail (RtpTimer * timer, RtpTimer * tail)
141 {
142   RtpTimer *next = rtp_timer_get_next (timer);
143   GstClockTimeDiff tail_delta = 0;
144   GstClockTimeDiff next_delta = 0;
145 
146   if (next == NULL)
147     return FALSE;
148 
149   if (rtp_timer_is_later (timer, tail))
150     return TRUE;
151 
152   if (rtp_timer_is_sooner (timer, next))
153     return FALSE;
154 
155   if (tail->timeout == next->timeout) {
156     gint tail_gap, next_gap;
157 
158     tail_gap = gst_rtp_buffer_compare_seqnum (timer->seqnum, tail->seqnum);
159     next_gap = gst_rtp_buffer_compare_seqnum (next->seqnum, timer->seqnum);
160 
161     if (tail_gap < next_gap)
162       return TRUE;
163   }
164 
165   if (GST_CLOCK_TIME_IS_VALID (timer->timeout) &&
166       GST_CLOCK_TIME_IS_VALID (next->timeout)) {
167     tail_delta = GST_CLOCK_DIFF (timer->timeout, tail->timeout);
168     next_delta = GST_CLOCK_DIFF (next->timeout, timer->timeout);
169 
170     if (tail_delta < next_delta)
171       return TRUE;
172   }
173 
174   return FALSE;
175 }
176 
177 static inline RtpTimer *
rtp_timer_queue_get_tail(RtpTimerQueue * queue)178 rtp_timer_queue_get_tail (RtpTimerQueue * queue)
179 {
180   return (RtpTimer *) queue->timers.tail;
181 }
182 
183 static inline void
rtp_timer_queue_set_tail(RtpTimerQueue * queue,RtpTimer * timer)184 rtp_timer_queue_set_tail (RtpTimerQueue * queue, RtpTimer * timer)
185 {
186   queue->timers.tail = (GList *) timer;
187   g_assert (queue->timers.tail->next == NULL);
188 }
189 
190 static inline RtpTimer *
rtp_timer_queue_get_head(RtpTimerQueue * queue)191 rtp_timer_queue_get_head (RtpTimerQueue * queue)
192 {
193   return (RtpTimer *) queue->timers.head;
194 }
195 
196 static inline void
rtp_timer_queue_set_head(RtpTimerQueue * queue,RtpTimer * timer)197 rtp_timer_queue_set_head (RtpTimerQueue * queue, RtpTimer * timer)
198 {
199   queue->timers.head = (GList *) timer;
200   g_assert (queue->timers.head->prev == NULL);
201 }
202 
203 static void
rtp_timer_queue_insert_before(RtpTimerQueue * queue,RtpTimer * sibling,RtpTimer * timer)204 rtp_timer_queue_insert_before (RtpTimerQueue * queue, RtpTimer * sibling,
205     RtpTimer * timer)
206 {
207   if (sibling == rtp_timer_queue_get_head (queue)) {
208     rtp_timer_queue_set_head (queue, timer);
209   } else {
210     rtp_timer_set_prev (timer, rtp_timer_get_prev (sibling));
211     rtp_timer_set_next (rtp_timer_get_prev (sibling), timer);
212   }
213 
214   rtp_timer_set_next (timer, sibling);
215   rtp_timer_set_prev (sibling, timer);
216 
217   queue->timers.length++;
218 }
219 
220 static void
rtp_timer_queue_insert_after(RtpTimerQueue * queue,RtpTimer * sibling,RtpTimer * timer)221 rtp_timer_queue_insert_after (RtpTimerQueue * queue, RtpTimer * sibling,
222     RtpTimer * timer)
223 {
224   if (sibling == rtp_timer_queue_get_tail (queue)) {
225     rtp_timer_queue_set_tail (queue, timer);
226   } else {
227     rtp_timer_set_next (timer, rtp_timer_get_next (sibling));
228     rtp_timer_set_prev (rtp_timer_get_next (sibling), timer);
229   }
230 
231   rtp_timer_set_prev (timer, sibling);
232   rtp_timer_set_next (sibling, timer);
233 
234   queue->timers.length++;
235 }
236 
237 static void
rtp_timer_queue_insert_tail(RtpTimerQueue * queue,RtpTimer * timer)238 rtp_timer_queue_insert_tail (RtpTimerQueue * queue, RtpTimer * timer)
239 {
240   RtpTimer *it = rtp_timer_queue_get_tail (queue);
241 
242   while (it) {
243     if (!GST_CLOCK_TIME_IS_VALID (it->timeout))
244       break;
245 
246     if (timer->timeout > it->timeout)
247       break;
248 
249     if (timer->timeout == it->timeout &&
250         gst_rtp_buffer_compare_seqnum (timer->seqnum, it->seqnum) < 0)
251       break;
252 
253     it = rtp_timer_get_prev (it);
254   }
255 
256   /* the queue is empty, or this is the earliest timeout */
257   if (it == NULL)
258     g_queue_push_head_link (&queue->timers, (GList *) timer);
259   else
260     rtp_timer_queue_insert_after (queue, it, timer);
261 }
262 
263 static void
rtp_timer_queue_insert_head(RtpTimerQueue * queue,RtpTimer * timer)264 rtp_timer_queue_insert_head (RtpTimerQueue * queue, RtpTimer * timer)
265 {
266   RtpTimer *it = rtp_timer_queue_get_head (queue);
267 
268   while (it) {
269     if (GST_CLOCK_TIME_IS_VALID (it->timeout)) {
270       if (!GST_CLOCK_TIME_IS_VALID (timer->timeout))
271         break;
272 
273       if (timer->timeout < it->timeout)
274         break;
275     }
276 
277     if (timer->timeout == it->timeout &&
278         gst_rtp_buffer_compare_seqnum (timer->seqnum, it->seqnum) > 0)
279       break;
280 
281     it = rtp_timer_get_next (it);
282   }
283 
284   /* the queue is empty, or this is the oldest */
285   if (it == NULL)
286     g_queue_push_tail_link (&queue->timers, (GList *) timer);
287   else
288     rtp_timer_queue_insert_before (queue, it, timer);
289 }
290 
291 static void
rtp_timer_queue_init(RtpTimerQueue * queue)292 rtp_timer_queue_init (RtpTimerQueue * queue)
293 {
294   queue->hashtable = g_hash_table_new (NULL, NULL);
295 }
296 
297 static void
rtp_timer_queue_finalize(GObject * object)298 rtp_timer_queue_finalize (GObject * object)
299 {
300   RtpTimerQueue *queue = RTP_TIMER_QUEUE (object);
301   RtpTimer *timer;
302 
303   while ((timer = rtp_timer_queue_pop_until (queue, GST_CLOCK_TIME_NONE)))
304     rtp_timer_free (timer);
305   g_hash_table_unref (queue->hashtable);
306   g_assert (queue->timers.length == 0);
307 }
308 
309 static void
rtp_timer_queue_class_init(RtpTimerQueueClass * klass)310 rtp_timer_queue_class_init (RtpTimerQueueClass * klass)
311 {
312   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
313   gobject_class->finalize = rtp_timer_queue_finalize;
314 }
315 
316 /**
317  * rtp_timer_free:
318  *
319  * Free a #RtpTimer structure. This should be used after a timer has been
320  * poped or unscheduled. The timer must be queued.
321  */
322 void
rtp_timer_free(RtpTimer * timer)323 rtp_timer_free (RtpTimer * timer)
324 {
325   g_return_if_fail (timer);
326   g_return_if_fail (timer->queued == FALSE);
327   g_return_if_fail (timer->list.next == NULL);
328   g_return_if_fail (timer->list.prev == NULL);
329 
330   g_slice_free (RtpTimer, timer);
331 }
332 
333 /**
334  * rtp_timer_dup:
335  * @timer: a #RtpTimer
336  *
337  * This allow cloning a #RtpTimer structure.
338  *
339  * Returns: a copy of @timer
340  */
341 RtpTimer *
rtp_timer_dup(const RtpTimer * timer)342 rtp_timer_dup (const RtpTimer * timer)
343 {
344   RtpTimer *copy = g_slice_new (RtpTimer);
345   memcpy (copy, timer, sizeof (RtpTimer));
346   memset (&copy->list, 0, sizeof (GList));
347   copy->queued = FALSE;
348   return copy;
349 }
350 
351 /**
352  * rtp_timer_queue_find:
353  * @queue: the #RtpTimerQueue object
354  * @seqnum: the sequence number of the #RtpTimer
355  *
356  * Lookup for a timer with @seqnum. Only one timer per seqnum exist withing
357  * the #RtpTimerQueue. This operation is o(1).
358  *
359  * Rerturn: the matching #RtpTimer or %NULL
360  */
361 RtpTimer *
rtp_timer_queue_find(RtpTimerQueue * queue,guint seqnum)362 rtp_timer_queue_find (RtpTimerQueue * queue, guint seqnum)
363 {
364   return g_hash_table_lookup (queue->hashtable, GINT_TO_POINTER (seqnum));
365 }
366 
367 /**
368  * rtp_timer_queue_peek_earliest:
369  * @queue: the #RtpTimerQueue object
370  *
371  * Rerturns: the #RtpTimer with earliest timeout value
372  */
373 RtpTimer *
rtp_timer_queue_peek_earliest(RtpTimerQueue * queue)374 rtp_timer_queue_peek_earliest (RtpTimerQueue * queue)
375 {
376   return rtp_timer_queue_get_head (queue);
377 }
378 
379 
380 /**
381  * rtp_timer_queue_new:
382  *
383  * Returns: a freshly allocated #RtpTimerQueue
384  */
385 RtpTimerQueue *
rtp_timer_queue_new(void)386 rtp_timer_queue_new (void)
387 {
388   return g_object_new (RTP_TYPE_TIMER_QUEUE, NULL);
389 }
390 
391 /**
392  * rtp_timer_queue_insert:
393  * @queue: the #RtpTimerQueue object
394  * @timer: (transfer full): the #RtpTimer to insert
395  *
396  * Insert a timer into the queue. Earliest timer are at the head and then
397  * timer are sorted by seqnum (smaller seqnum first). This function is o(n)
398  * but it is expected that most timers added are schedule later, in which case
399  * the insertion will be faster.
400  *
401  * Returns: %FALSE if a timer with the same seqnum already existed
402  */
403 gboolean
rtp_timer_queue_insert(RtpTimerQueue * queue,RtpTimer * timer)404 rtp_timer_queue_insert (RtpTimerQueue * queue, RtpTimer * timer)
405 {
406   g_return_val_if_fail (timer->queued == FALSE, FALSE);
407 
408   if (rtp_timer_queue_find (queue, timer->seqnum)) {
409     rtp_timer_free (timer);
410     GST_WARNING ("Timer queue collision, freeing duplicate.");
411     return FALSE;
412   }
413 
414   if (timer->timeout == -1)
415     rtp_timer_queue_insert_head (queue, timer);
416   else
417     rtp_timer_queue_insert_tail (queue, timer);
418 
419   g_hash_table_insert (queue->hashtable,
420       GINT_TO_POINTER (timer->seqnum), timer);
421   timer->queued = TRUE;
422 
423   return TRUE;
424 }
425 
426 /**
427  * rtp_timer_queue_reschedule:
428  * @queue: the #RtpTimerQueue object
429  * @timer: the #RtpTimer to reschedule
430  *
431  * This function moves @timer inside the queue to put it back to it's new
432  * location. This function is o(n) but it is assumed that nearby modification
433  * of the timeout will occure.
434  *
435  * Returns: %TRUE if the timer was moved
436  */
437 gboolean
rtp_timer_queue_reschedule(RtpTimerQueue * queue,RtpTimer * timer)438 rtp_timer_queue_reschedule (RtpTimerQueue * queue, RtpTimer * timer)
439 {
440   RtpTimer *it = timer;
441 
442   g_return_val_if_fail (timer->queued == TRUE, FALSE);
443 
444   if (rtp_timer_is_closer_to_head (timer, rtp_timer_queue_get_head (queue))) {
445     g_queue_unlink (&queue->timers, (GList *) timer);
446     rtp_timer_queue_insert_head (queue, timer);
447     return TRUE;
448   }
449 
450   while (rtp_timer_is_sooner (timer, rtp_timer_get_prev (it)))
451     it = rtp_timer_get_prev (it);
452 
453   if (it != timer) {
454     g_queue_unlink (&queue->timers, (GList *) timer);
455     rtp_timer_queue_insert_before (queue, it, timer);
456     return TRUE;
457   }
458 
459   if (rtp_timer_is_closer_to_tail (timer, rtp_timer_queue_get_tail (queue))) {
460     g_queue_unlink (&queue->timers, (GList *) timer);
461     rtp_timer_queue_insert_tail (queue, timer);
462     return TRUE;
463   }
464 
465   while (rtp_timer_is_later (timer, rtp_timer_get_next (it)))
466     it = rtp_timer_get_next (it);
467 
468   if (it != timer) {
469     g_queue_unlink (&queue->timers, (GList *) timer);
470     rtp_timer_queue_insert_after (queue, it, timer);
471     return TRUE;
472   }
473 
474   return FALSE;
475 }
476 
477 /**
478  * rtp_timer_queue_unschedule:
479  * @queue: the #RtpTimerQueue
480  * @timer: the #RtpTimer to unschedule
481  *
482  * This removes a timer from the queue. The timer structure can be reused,
483  * or freed using rtp_timer_free(). This function is o(1).
484  */
485 void
rtp_timer_queue_unschedule(RtpTimerQueue * queue,RtpTimer * timer)486 rtp_timer_queue_unschedule (RtpTimerQueue * queue, RtpTimer * timer)
487 {
488   g_return_if_fail (timer->queued == TRUE);
489 
490   g_queue_unlink (&queue->timers, (GList *) timer);
491   g_hash_table_remove (queue->hashtable, GINT_TO_POINTER (timer->seqnum));
492   timer->queued = FALSE;
493 }
494 
495 /**
496  * rtp_timer_queue_pop_until:
497  * @queue: the #RtpTimerQueue
498  * @timeout: Time at witch timers expired
499  *
500  * Unschdedule and return the earliest packet that has a timeout smaller or
501  * equal to @timeout. The returns #RtpTimer must be freed with
502  * rtp_timer_free(). This function is o(1).
503  *
504  * Returns: an expired timer according to @timeout, or %NULL.
505  */
506 RtpTimer *
rtp_timer_queue_pop_until(RtpTimerQueue * queue,GstClockTime timeout)507 rtp_timer_queue_pop_until (RtpTimerQueue * queue, GstClockTime timeout)
508 {
509   RtpTimer *timer;
510 
511   timer = (RtpTimer *) g_queue_peek_head_link (&queue->timers);
512   if (!timer)
513     return NULL;
514 
515   if (!GST_CLOCK_TIME_IS_VALID (timer->timeout) || timer->timeout <= timeout) {
516     rtp_timer_queue_unschedule (queue, timer);
517     return timer;
518   }
519 
520   return NULL;
521 }
522 
523 /**
524  * rtp_timer_queue_remove_until:
525  * @queue: the #RtpTimerQueue
526  * @timeout: Time at witch timers expired
527  *
528  * Unschedule and free all timers that has a timeout smaller or equal to
529  * @timeout.
530  */
531 void
rtp_timer_queue_remove_until(RtpTimerQueue * queue,GstClockTime timeout)532 rtp_timer_queue_remove_until (RtpTimerQueue * queue, GstClockTime timeout)
533 {
534   RtpTimer *timer;
535 
536   while ((timer = rtp_timer_queue_pop_until (queue, timeout))) {
537     GST_LOG ("Removing expired timer #%d, %" GST_TIME_FORMAT " < %"
538         GST_TIME_FORMAT, timer->seqnum, GST_TIME_ARGS (timer->timeout),
539         GST_TIME_ARGS (timeout));
540     rtp_timer_free (timer);
541   }
542 }
543 
544 /**
545  * rtp_timer_queue_remove_all:
546  * @queue: the #RtpTimerQueue
547  *
548  * Unschedule and free all timers from the queue.
549  */
550 void
rtp_timer_queue_remove_all(RtpTimerQueue * queue)551 rtp_timer_queue_remove_all (RtpTimerQueue * queue)
552 {
553   rtp_timer_queue_remove_until (queue, GST_CLOCK_TIME_NONE);
554 }
555 
556 /**
557  * rtp_timer_queue_set_timer:
558  * @queue: the #RtpTimerQueue
559  * @type: the #RtpTimerType
560  * @senum: the timer seqnum
561  * @timeout: the timer timeout
562  * @delay: the additional delay (will be added to @timeout)
563  * @duration: the duration of the event related to the timer
564  * @offset: offset that can be used to convert the timeout to timestamp
565  *
566  * If there exist a timer with this seqnum it will be updated other a new
567  * timer is created and inserted into the queue. This function is o(n) except
568  * that it's optimized for later timer insertion.
569  */
570 void
rtp_timer_queue_set_timer(RtpTimerQueue * queue,RtpTimerType type,guint16 seqnum,GstClockTime timeout,GstClockTime delay,GstClockTime duration,GstClockTimeDiff offset)571 rtp_timer_queue_set_timer (RtpTimerQueue * queue, RtpTimerType type,
572     guint16 seqnum, GstClockTime timeout, GstClockTime delay,
573     GstClockTime duration, GstClockTimeDiff offset)
574 {
575   RtpTimer *timer;
576 
577   timer = rtp_timer_queue_find (queue, seqnum);
578   if (!timer)
579     timer = rtp_timer_new ();
580 
581   /* for new timers or on seqnum change reset the RTX data */
582   if (!timer->queued || timer->seqnum != seqnum) {
583     if (type == RTP_TIMER_EXPECTED) {
584       timer->rtx_base = timeout;
585     }
586 
587     timer->rtx_last = GST_CLOCK_TIME_NONE;
588     timer->num_rtx_retry = 0;
589     timer->num_rtx_received = 0;
590   }
591 
592   timer->type = type;
593   timer->seqnum = seqnum;
594 
595   if (timeout == -1)
596     timer->timeout = -1;
597   else
598     timer->timeout = timeout + delay + offset;
599 
600   timer->offset = offset;
601   timer->duration = duration;
602 
603   if (timer->queued)
604     rtp_timer_queue_reschedule (queue, timer);
605   else
606     rtp_timer_queue_insert (queue, timer);
607 }
608 
609 /**
610  * rtp_timer_queue_set_expected:
611  * @queue: the #RtpTimerQueue
612  * @senum: the timer seqnum
613  * @timeout: the timer timeout
614  * @delay: the additional delay (will be added to @timeout)
615  * @duration: the duration of the event related to the timer
616  *
617  * Specialized version of rtp_timer_queue_set_timer() that creates or updates a
618  * timer with type %RTP_TIMER_EXPECTED. Expected timers do not carry
619  * a timestamp, hence have no offset.
620  */
621 void
rtp_timer_queue_set_expected(RtpTimerQueue * queue,guint16 seqnum,GstClockTime timeout,GstClockTime delay,GstClockTime duration)622 rtp_timer_queue_set_expected (RtpTimerQueue * queue, guint16 seqnum,
623     GstClockTime timeout, GstClockTime delay, GstClockTime duration)
624 {
625   rtp_timer_queue_set_timer (queue, RTP_TIMER_EXPECTED, seqnum, timeout,
626       delay, duration, 0);
627 }
628 
629 /**
630  * rtp_timer_queue_set_lost:
631  * @queue: the #RtpTimerQueue
632  * @senum: the timer seqnum
633  * @timeout: the timer timeout
634  * @duration: the duration of the event related to the timer
635  * @offset: offset that can be used to convert the timeout to timestamp
636  *
637  * Specialized version of rtp_timer_queue_set_timer() that creates or updates a
638  * timer with type %RTP_TIMER_LOST.
639  */
640 void
rtp_timer_queue_set_lost(RtpTimerQueue * queue,guint16 seqnum,GstClockTime timeout,GstClockTime duration,GstClockTimeDiff offset)641 rtp_timer_queue_set_lost (RtpTimerQueue * queue, guint16 seqnum,
642     GstClockTime timeout, GstClockTime duration, GstClockTimeDiff offset)
643 {
644   rtp_timer_queue_set_timer (queue, RTP_TIMER_LOST, seqnum, timeout, 0,
645       duration, offset);
646 }
647 
648 /**
649  * rtp_timer_queue_set_eos:
650  * @queue: the #RtpTimerQueue
651  * @timeout: the timer timeout
652  * @offset: offset that can be used to convert the timeout to timestamp
653  *
654  * Specialized version of rtp_timer_queue_set_timer() that creates or updates a
655  * timer with type %RTP_TIMER_EOS. There is only one such a timer and it has
656  * the special seqnum value -1 (FIXME this is not an invalid seqnum,).
657  */
658 void
rtp_timer_queue_set_eos(RtpTimerQueue * queue,GstClockTime timeout,GstClockTimeDiff offset)659 rtp_timer_queue_set_eos (RtpTimerQueue * queue, GstClockTime timeout,
660     GstClockTimeDiff offset)
661 {
662   rtp_timer_queue_set_timer (queue, RTP_TIMER_EOS, -1, timeout, 0, 0, offset);
663 }
664 
665 /**
666  * rtp_timer_queue_set_deadline:
667  * @queue: the #RtpTimerQueue
668  * @senum: the timer seqnum
669  * @timeout: the timer timeout
670  * @offset: offset that can be used to convert the timeout to timestamp
671  *
672  * Specialized version of rtp_timer_queue_set_timer() that creates or updates a
673  * timer with type %RTP_TIMER_DEADLINE. There should be only one such a timer,
674  * its seqnum matches the first packet to be output.
675  */
676 void
rtp_timer_queue_set_deadline(RtpTimerQueue * queue,guint16 seqnum,GstClockTime timeout,GstClockTimeDiff offset)677 rtp_timer_queue_set_deadline (RtpTimerQueue * queue, guint16 seqnum,
678     GstClockTime timeout, GstClockTimeDiff offset)
679 {
680   rtp_timer_queue_set_timer (queue, RTP_TIMER_DEADLINE, seqnum, timeout, 0,
681       0, offset);
682 }
683 
684 /**
685  * rtp_timer_queue_update_timer:
686  * @queue: the #RtpTimerQueue
687  * @senum: the timer seqnum
688  * @timeout: the timer timeout
689  * @delay: the additional delay (will be added to @timeout)
690  * @offset: offset that can be used to convert the timeout to timestamp
691  * @reset: if the RTX statistics should be reset
692  *
693  * A utility to update an already queued timer.
694  */
695 void
rtp_timer_queue_update_timer(RtpTimerQueue * queue,RtpTimer * timer,guint16 seqnum,GstClockTime timeout,GstClockTime delay,GstClockTimeDiff offset,gboolean reset)696 rtp_timer_queue_update_timer (RtpTimerQueue * queue, RtpTimer * timer,
697     guint16 seqnum, GstClockTime timeout, GstClockTime delay,
698     GstClockTimeDiff offset, gboolean reset)
699 {
700   g_return_if_fail (timer != NULL);
701 
702   if (reset) {
703     GST_DEBUG ("reset rtx base %" GST_TIME_FORMAT "->%" GST_TIME_FORMAT,
704         GST_TIME_ARGS (timer->rtx_base), GST_TIME_ARGS (timeout));
705     timer->rtx_base = timeout;
706   }
707 
708   if (timer->seqnum != seqnum) {
709     timer->num_rtx_retry = 0;
710     timer->num_rtx_received = 0;
711 
712     if (timer->queued) {
713       g_hash_table_remove (queue->hashtable, GINT_TO_POINTER (timer->seqnum));
714       g_hash_table_insert (queue->hashtable, GINT_TO_POINTER (seqnum), timer);
715     }
716   }
717 
718   if (timeout == -1)
719     timer->timeout = -1;
720   else
721     timer->timeout = timeout + delay + offset;
722 
723   timer->seqnum = seqnum;
724   timer->offset = offset;
725 
726   if (timer->queued)
727     rtp_timer_queue_reschedule (queue, timer);
728   else
729     rtp_timer_queue_insert (queue, timer);
730 }
731 
732 /**
733  * rtp_timer_queue_length:
734  * @queue: the #RtpTimerQueue
735  *
736  * Returns: the number of timers in the #RtpTimerQueue
737  */
738 guint
rtp_timer_queue_length(RtpTimerQueue * queue)739 rtp_timer_queue_length (RtpTimerQueue * queue)
740 {
741   return queue->timers.length;
742 }
743