/* GStreamer * Copyright (C) <2007> Wim Taymans * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include "rtpjitterbuffer.h" GST_DEBUG_CATEGORY_STATIC (rtp_jitter_buffer_debug); #define GST_CAT_DEFAULT rtp_jitter_buffer_debug /* signals and args */ enum { LAST_SIGNAL }; enum { PROP_0 }; /* GObject vmethods */ static void rtp_jitter_buffer_finalize (GObject * object); /* static guint rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */ G_DEFINE_TYPE (RTPJitterBuffer, rtp_jitter_buffer, G_TYPE_OBJECT); static void rtp_jitter_buffer_class_init (RTPJitterBufferClass * klass) { GObjectClass *gobject_class; gobject_class = (GObjectClass *) klass; gobject_class->finalize = rtp_jitter_buffer_finalize; GST_DEBUG_CATEGORY_INIT (rtp_jitter_buffer_debug, "rtpjitterbuffer", 0, "RTP Jitter Buffer"); } static void rtp_jitter_buffer_init (RTPJitterBuffer * jbuf) { jbuf->packets = g_queue_new (); } static void rtp_jitter_buffer_finalize (GObject * object) { RTPJitterBuffer *jbuf; jbuf = RTP_JITTER_BUFFER_CAST (object); rtp_jitter_buffer_flush (jbuf); g_queue_free (jbuf->packets); G_OBJECT_CLASS (rtp_jitter_buffer_parent_class)->finalize (object); } /** * rtp_jitter_buffer_new: * * Create an #RTPJitterBuffer. * * Returns: a new #RTPJitterBuffer. Use g_object_unref() after usage. */ RTPJitterBuffer * rtp_jitter_buffer_new (void) { RTPJitterBuffer *jbuf; jbuf = g_object_new (RTP_TYPE_JITTER_BUFFER, NULL); return jbuf; } static gint compare_seqnum (GstBuffer * a, GstBuffer * b, RTPJitterBuffer * jbuf) { guint16 seq1, seq2; seq1 = gst_rtp_buffer_get_seq (a); seq2 = gst_rtp_buffer_get_seq (b); /* check if diff more than half of the 16bit range */ if (abs (seq2 - seq1) > (1 << 15)) { /* one of a/b has wrapped */ return seq1 - seq2; } else { return seq2 - seq1; } } /** * rtp_jitter_buffer_insert: * @jbuf: an #RTPJitterBuffer * @buf: a buffer * * 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. * * Returns: %FALSE if a packet with the same number already existed. */ gboolean rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf) { GList *list; gint func_ret = 1; g_return_val_if_fail (jbuf != NULL, FALSE); g_return_val_if_fail (buf != NULL, FALSE); /* loop the list to skip strictly smaller seqnum buffers */ list = jbuf->packets->head; while (list && (func_ret = compare_seqnum (GST_BUFFER_CAST (list->data), buf, jbuf)) < 0) list = list->next; /* we hit a packet with the same seqnum, return FALSE to notify a duplicate */ if (func_ret == 0) return FALSE; if (list) g_queue_insert_before (jbuf->packets, list, buf); else g_queue_push_tail (jbuf->packets, buf); return TRUE; } /** * rtp_jitter_buffer_pop: * @jbuf: an #RTPJitterBuffer * * Pops the oldest buffer from the packet queue of @jbuf. * * Returns: a #GstBuffer or %NULL when there was no packet in the queue. */ GstBuffer * rtp_jitter_buffer_pop (RTPJitterBuffer * jbuf) { GstBuffer *buf; g_return_val_if_fail (jbuf != NULL, FALSE); buf = g_queue_pop_tail (jbuf->packets); return buf; } /** * rtp_jitter_buffer_flush: * @jbuf: an #RTPJitterBuffer * * Flush all packets from the jitterbuffer. */ void rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf) { GstBuffer *buffer; g_return_if_fail (jbuf != NULL); while ((buffer = g_queue_pop_head (jbuf->packets))) gst_buffer_unref (buffer); } /** * rtp_jitter_buffer_num_packets: * @jbuf: an #RTPJitterBuffer * * Get the number of packets currently in "jbuf. * * Returns: The number of packets in @jbuf. */ guint rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf) { g_return_val_if_fail (jbuf != NULL, 0); return jbuf->packets->length; } /** * rtp_jitter_buffer_get_ts_diff: * @jbuf: an #RTPJitterBuffer * * Get the difference between the timestamps of first and last packet in the * jitterbuffer. * * Returns: The difference expressed in the timestamp units of the packets. */ guint32 rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf) { guint32 high_ts, low_ts; GstBuffer *high_buf, *low_buf; g_return_val_if_fail (jbuf != NULL, 0); high_buf = g_queue_peek_head (jbuf->packets); low_buf = g_queue_peek_tail (jbuf->packets); if (!high_buf || !low_buf || high_buf == low_buf) return 0; high_ts = gst_rtp_buffer_get_timestamp (high_buf); low_ts = gst_rtp_buffer_get_timestamp (low_buf); /* it needs to work if ts wraps */ if (high_ts >= low_ts) { return high_ts - low_ts; } else { return high_ts + G_MAXUINT32 + 1 - low_ts; } }