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 (©->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