Beispiel #1
0
static void meh_screen_popup_favorite_toggle(App* app, Screen* screen) {
    g_assert(app != NULL);
    g_assert(screen != NULL);

    PopupData* data = meh_screen_popup_get_data(screen);
    ExecutableListData* exec_list_data = meh_exec_list_get_data(data->src_screen);

    /* updates the value of the executable */

    gboolean new_value = data->executable->favorite == 1 ? FALSE : TRUE;

    if (meh_db_set_executable_favorite(app->db, data->executable, new_value)) {
        data->executable->favorite = new_value;
    }

    /* re-position the executable in the executables list if necessary */
    if (g_queue_get_length(exec_list_data->executables) > 1) {
        int prev_selected = exec_list_data->selected_executable;

        unsigned int i = 0;

        /* retrieves the one which will move in the list */
        Executable* to_move = g_queue_pop_nth(exec_list_data->executables, exec_list_data->selected_executable);

        /* find the good position for the moved executable */

        for (i = 0; i < g_queue_get_length(exec_list_data->executables); i++) {
            gboolean exit = FALSE;
            Executable* ex = g_queue_peek_nth(exec_list_data->executables, i);
            /* if favorite, ensure to stay in the favorite zone */
            if (new_value == TRUE) {
                if (ex->favorite == FALSE)  {
                    exit = TRUE;
                }
            }

            gchar* first = g_utf8_strup(ex->display_name, g_utf8_strlen(ex->display_name, -1));
            gchar* second = g_utf8_strup(data->executable->display_name, g_utf8_strlen(ex->display_name, -1));

            if (g_utf8_collate(first, second) > 0) {
                if (new_value == TRUE && ex->favorite == TRUE) {
                    exit = TRUE;
                }
                else if (new_value == FALSE && ex->favorite == FALSE) {
                    exit = TRUE;
                }
            }

            g_free(first);
            g_free(second);

            if (exit) {
                break;
            }
        }

        GList* after = g_queue_peek_nth_link(exec_list_data->executables, i);

        /* re-add it to the good position */

        g_queue_insert_before(exec_list_data->executables, after, to_move);

        /* notify the screen of the new selected executable */

        exec_list_data->selected_executable = i;

        /* redraw the executables list texts */

        meh_exec_list_refresh_executables_widget(app, data->src_screen);

        /* move and redraw the selection */

        meh_exec_list_after_cursor_move(app, data->src_screen, prev_selected);
    }

    /* finally close the popup */

    meh_screen_popup_close(screen);
}
static GList *
priority_segment_alloc_node (TrackerPriorityQueue *queue,
                             gint                  priority)
{
	PrioritySegment *segment = NULL;
	gboolean found = FALSE;
	gint l, r, c;
	GList *node;

	/* Perform binary search to find out the segment for
	 * the given priority, create one if it isn't found.
	 */
	l = 0;
	r = queue->segments->len - 1;

	while (queue->segments->len > 0 && !found) {
		c = (r + l) / 2;
		segment = &g_array_index (queue->segments, PrioritySegment, c);

		if (segment->priority == priority) {
			found = TRUE;
			break;
		} else if (segment->priority > priority) {
			r = c - 1;
		} else if (segment->priority < priority) {
			l = c + 1;
		}

		if (l > r) {
			break;
		}
	}

	if (found) {
		/* Element found, append at the end of segment */
		g_assert (segment != NULL);
		g_assert (segment->priority == priority);

		g_queue_insert_after (&queue->queue, segment->last_elem, NULL);
		node = segment->last_elem = segment->last_elem->next;
	} else {
		PrioritySegment new_segment = { 0 };

		new_segment.priority = priority;

		if (segment) {
			g_assert (segment->priority != priority);

			/* Binary search got to one of the closest results,
			 * but we may have come from either of both sides,
			 * so check whether we have to insert after the
			 * segment we got.
			 */
			if (segment->priority > priority) {
				/* We have to insert to the left of this element */
				g_queue_insert_before (&queue->queue, segment->first_elem, NULL);
				node = segment->first_elem->prev;
			} else {
				/* We have to insert to the right of this element */
				g_queue_insert_after (&queue->queue, segment->last_elem, NULL);
				node = segment->last_elem->next;
				c++;
			}

			new_segment.first_elem = new_segment.last_elem = node;
			g_array_insert_val (queue->segments, c, new_segment);
		} else {
			/* Segments list has 0 elements */
			g_assert (queue->segments->len == 0);
			g_assert (g_queue_get_length (&queue->queue) == 0);

			node = g_list_alloc ();
			g_queue_push_head_link (&queue->queue, node);
			new_segment.first_elem = new_segment.last_elem = node;

			g_array_append_val (queue->segments, new_segment);
		}
	}

	return node;
}
Beispiel #3
0
/**
 * 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;
  }
}
Beispiel #4
0
/**
 * rdt_jitter_buffer_insert:
 * @jbuf: an #RDTJitterBuffer
 * @buf: a buffer
 * @time: a running_time when this buffer was received in nanoseconds
 * @clock_rate: the clock-rate of the payload 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
rdt_jitter_buffer_insert (RDTJitterBuffer * jbuf, GstBuffer * buf,
    GstClockTime time, guint32 clock_rate, gboolean * tail)
{
  GList *list;
  guint32 rtptime;
  guint16 seqnum;
  GstRDTPacket packet;
  gboolean more;

  g_return_val_if_fail (jbuf != NULL, FALSE);
  g_return_val_if_fail (buf != NULL, FALSE);

  more = gst_rdt_buffer_get_first_packet (buf, &packet);
  /* programmer error */
  g_return_val_if_fail (more == TRUE, FALSE);

  seqnum = gst_rdt_packet_data_get_seq (&packet);
  /* do skew calculation by measuring the difference between rtptime and the
   * receive time, this function will retimestamp @buf with the skew corrected
   * running time. */
  rtptime = gst_rdt_packet_data_get_timestamp (&packet);

  /* loop the list to skip strictly smaller seqnum buffers */
  for (list = jbuf->packets->head; list; list = g_list_next (list)) {
    guint16 qseq;
    gint gap;

    more =
        gst_rdt_buffer_get_first_packet (GST_BUFFER_CAST (list->data), &packet);
    /* programmer error */
    g_return_val_if_fail (more == TRUE, FALSE);

    qseq = gst_rdt_packet_data_get_seq (&packet);

    /* compare the new seqnum to the one in the buffer */
    gap = gst_rdt_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;
  }


  if (clock_rate) {
    time = calculate_skew (jbuf, rtptime, time, clock_rate);
    GST_BUFFER_TIMESTAMP (buf) = time;
  }

  if (list)
    g_queue_insert_before (jbuf->packets, list, buf);
  else
    g_queue_push_tail (jbuf->packets, buf);

  /* tail was changed when we did not find a previous packet, we set the return
   * flag when requested. */
  if (tail)
    *tail = (list == NULL);

  return TRUE;

  /* ERRORS */
duplicate:
  {
    GST_WARNING ("duplicate packet %d found", (gint) seqnum);
    return FALSE;
  }
}
static GstFlowReturn
gst_live_live_adder_chain (GstPad * pad, GstBuffer * buffer)
{
    GstLiveAdder *adder = GST_LIVE_ADDER (gst_pad_get_parent_element (pad));
    GstLiveAdderPadPrivate *padprivate = NULL;
    GstFlowReturn ret = GST_FLOW_OK;
    GList *item = NULL;
    GstClockTime skip = 0;
    gint64 drift = 0;             /* Positive if new buffer after old buffer */

    GST_OBJECT_LOCK (adder);

    ret = adder->srcresult;

    GST_DEBUG ("Incoming buffer time:%" GST_TIME_FORMAT " duration:%"
               GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
               GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));

    if (ret != GST_FLOW_OK) {
        GST_DEBUG_OBJECT (adder, "Passing non-ok result from src: %s",
                          gst_flow_get_name (ret));
        gst_buffer_unref (buffer);
        goto out;
    }

    padprivate = gst_pad_get_element_private (pad);

    if (!padprivate) {
        ret = GST_FLOW_NOT_LINKED;
        gst_buffer_unref (buffer);
        goto out;
    }

    if (padprivate->eos) {
        GST_DEBUG_OBJECT (adder, "Received buffer after EOS");
        ret = GST_FLOW_UNEXPECTED;
        gst_buffer_unref (buffer);
        goto out;
    }

    if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
        goto invalid_timestamp;

    if (padprivate->segment.format == GST_FORMAT_UNDEFINED) {
        GST_WARNING_OBJECT (adder, "No new-segment received,"
                            " initializing segment with time 0..-1");
        gst_segment_init (&padprivate->segment, GST_FORMAT_TIME);
        gst_segment_set_newsegment (&padprivate->segment,
                                    FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0);
    }

    if (padprivate->segment.format != GST_FORMAT_TIME)
        goto invalid_segment;

    buffer = gst_buffer_make_metadata_writable (buffer);

    drift = GST_BUFFER_TIMESTAMP (buffer) - padprivate->expected_timestamp;

    /* Just see if we receive invalid timestamp/durations */
    if (GST_CLOCK_TIME_IS_VALID (padprivate->expected_timestamp) &&
            !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT) &&
            (drift != 0)) {
        GST_LOG_OBJECT (adder,
                        "Timestamp discontinuity without the DISCONT flag set"
                        " (expected %" GST_TIME_FORMAT ", got %" GST_TIME_FORMAT
                        " drift:%" G_GINT64_FORMAT "ms)",
                        GST_TIME_ARGS (padprivate->expected_timestamp),
                        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), drift / GST_MSECOND);

        /* We accept drifts of 10ms */
        if (ABS (drift) < (10 * GST_MSECOND)) {
            GST_DEBUG ("Correcting minor drift");
            GST_BUFFER_TIMESTAMP (buffer) = padprivate->expected_timestamp;
        }
    }


    /* If there is no duration, lets set one */
    if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
        GST_BUFFER_DURATION (buffer) =
            gst_audio_duration_from_pad_buffer (pad, buffer);
        padprivate->expected_timestamp = GST_CLOCK_TIME_NONE;
    } else {
        padprivate->expected_timestamp = GST_BUFFER_TIMESTAMP (buffer) +
                                         GST_BUFFER_DURATION (buffer);
    }


    /*
     * Lets clip the buffer to the segment (so we don't have to worry about
     * cliping afterwards).
     * This should also guarantee us that we'll have valid timestamps and
     * durations afterwards
     */

    buffer = gst_audio_buffer_clip (buffer, &padprivate->segment, adder->rate,
                                    adder->bps);

    /* buffer can be NULL if it's completely outside of the segment */
    if (!buffer) {
        GST_DEBUG ("Buffer completely outside of configured segment, dropping it");
        goto out;
    }

    /*
     * Make sure all incoming buffers share the same timestamping
     */
    GST_BUFFER_TIMESTAMP (buffer) =
        gst_segment_to_running_time (&padprivate->segment,
                                     padprivate->segment.format, GST_BUFFER_TIMESTAMP (buffer));


    if (GST_CLOCK_TIME_IS_VALID (adder->next_timestamp) &&
            GST_BUFFER_TIMESTAMP (buffer) < adder->next_timestamp) {
        if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <
                adder->next_timestamp) {
            GST_DEBUG_OBJECT (adder, "Buffer is late, dropping (ts: %" GST_TIME_FORMAT
                              " duration: %" GST_TIME_FORMAT ")",
                              GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
                              GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
            gst_buffer_unref (buffer);
            goto out;
        } else {
            skip = adder->next_timestamp - GST_BUFFER_TIMESTAMP (buffer);
            GST_DEBUG_OBJECT (adder, "Buffer is partially late, skipping %"
                              GST_TIME_FORMAT, GST_TIME_ARGS (skip));
        }
    }

    /* If our new buffer's head is higher than the queue's head, lets wake up,
     * we may not have to wait for as long
     */
    if (adder->clock_id &&
            g_queue_peek_head (adder->buffers) != NULL &&
            GST_BUFFER_TIMESTAMP (buffer) + skip <
            GST_BUFFER_TIMESTAMP (g_queue_peek_head (adder->buffers)))
        gst_clock_id_unschedule (adder->clock_id);

    for (item = g_queue_peek_head_link (adder->buffers);
            item; item = g_list_next (item)) {
        GstBuffer *oldbuffer = item->data;
        GstClockTime old_skip = 0;
        GstClockTime mix_duration = 0;
        GstClockTime mix_start = 0;
        GstClockTime mix_end = 0;

        /* We haven't reached our place yet */
        if (GST_BUFFER_TIMESTAMP (buffer) + skip >=
                GST_BUFFER_TIMESTAMP (oldbuffer) + GST_BUFFER_DURATION (oldbuffer))
            continue;

        /* We're past our place, lets insert ouselves here */
        if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <=
                GST_BUFFER_TIMESTAMP (oldbuffer))
            break;

        /* if we reach this spot, we have overlap, so we must mix */

        /* First make a subbuffer with the non-overlapping part */
        if (GST_BUFFER_TIMESTAMP (buffer) + skip < GST_BUFFER_TIMESTAMP (oldbuffer)) {
            GstBuffer *subbuffer = NULL;
            GstClockTime subbuffer_duration = GST_BUFFER_TIMESTAMP (oldbuffer) -
                                              (GST_BUFFER_TIMESTAMP (buffer) + skip);

            subbuffer = gst_buffer_create_sub (buffer,
                                               gst_live_adder_length_from_duration (adder, skip),
                                               gst_live_adder_length_from_duration (adder, subbuffer_duration));

            GST_BUFFER_TIMESTAMP (subbuffer) = GST_BUFFER_TIMESTAMP (buffer) + skip;
            GST_BUFFER_DURATION (subbuffer) = subbuffer_duration;

            skip += subbuffer_duration;

            g_queue_insert_before (adder->buffers, item, subbuffer);
        }

        /* Now we are on the overlapping part */
        oldbuffer = gst_buffer_make_writable (oldbuffer);
        item->data = oldbuffer;

        old_skip = GST_BUFFER_TIMESTAMP (buffer) + skip -
                   GST_BUFFER_TIMESTAMP (oldbuffer);

        mix_start = GST_BUFFER_TIMESTAMP (oldbuffer) + old_skip;

        if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <
                GST_BUFFER_TIMESTAMP (oldbuffer) + GST_BUFFER_DURATION (oldbuffer))
            mix_end = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
        else
            mix_end = GST_BUFFER_TIMESTAMP (oldbuffer) +
                      GST_BUFFER_DURATION (oldbuffer);

        mix_duration = mix_end - mix_start;

        adder->func (GST_BUFFER_DATA (oldbuffer) +
                     gst_live_adder_length_from_duration (adder, old_skip),
                     GST_BUFFER_DATA (buffer) +
                     gst_live_adder_length_from_duration (adder, skip),
                     gst_live_adder_length_from_duration (adder, mix_duration));

        skip += mix_duration;
    }

    g_cond_broadcast (adder->not_empty_cond);

    if (skip == GST_BUFFER_DURATION (buffer)) {
        gst_buffer_unref (buffer);
    } else {
        if (skip) {
            GstClockTime subbuffer_duration = GST_BUFFER_DURATION (buffer) - skip;
            GstClockTime subbuffer_ts = GST_BUFFER_TIMESTAMP (buffer) + skip;
            GstBuffer *new_buffer = gst_buffer_create_sub (buffer,
                                    gst_live_adder_length_from_duration (adder, skip),
                                    gst_live_adder_length_from_duration (adder, subbuffer_duration));
            gst_buffer_unref (buffer);
            buffer = new_buffer;
            GST_BUFFER_TIMESTAMP (buffer) = subbuffer_ts;
            GST_BUFFER_DURATION (buffer) = subbuffer_duration;
        }

        if (item)
            g_queue_insert_before (adder->buffers, item, buffer);
        else
            g_queue_push_tail (adder->buffers, buffer);
    }

out:

    GST_OBJECT_UNLOCK (adder);
    gst_object_unref (adder);

    return ret;

invalid_timestamp:

    GST_OBJECT_UNLOCK (adder);
    gst_buffer_unref (buffer);
    GST_ELEMENT_ERROR (adder, STREAM, FAILED,
                       ("Buffer without a valid timestamp received"),
                       ("Invalid timestamp received on buffer"));

    return GST_FLOW_ERROR;

invalid_segment:
    {
        const gchar *format = gst_format_get_name (padprivate->segment.format);
        GST_OBJECT_UNLOCK (adder);
        gst_buffer_unref (buffer);
        GST_ELEMENT_ERROR (adder, STREAM, FAILED,
                           ("This element only supports TIME segments, received other type"),
                           ("Received a segment of type %s, only support time segment", format));

        return GST_FLOW_ERROR;
    }

}
Beispiel #6
0
/**
 * 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;

  g_return_val_if_fail (jbuf != NULL, FALSE);
  g_return_val_if_fail (buf != NULL, FALSE);

  seqnum = gst_rtp_buffer_get_seq (buf);

  /* loop the list to skip strictly smaller seqnum buffers */
  for (list = jbuf->packets->head; list; list = g_list_next (list)) {
    guint16 qseq;
    gint gap;

    qseq = gst_rtp_buffer_get_seq (GST_BUFFER_CAST (list->data));

    /* 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 (buf);
  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);

  return TRUE;

  /* ERRORS */
duplicate:
  {
    GST_WARNING ("duplicate packet %d found", (gint) seqnum);
    return FALSE;
  }
}