gstreamer中的rtpjitterbuffer代码分析:推送线程

来源:互联网 发布:毕向东35天java教学 编辑:程序博客网 时间:2024/05/29 13:33

1. 简介:

    本文主要分析gstreamer中的rtpjitterbuffer中推送数据线程的代码。

2. 流程:

    推送线程主要功能就是根据jbuf上的数据,对排序的包(或者对应包序的异常事件)进行处理下发。

    主要流程如下:

    1) 当rtpjitterbuffer工作在PUSH模式下,会通过gst_pad_start_task创建一个子线程,执行推送数据主要操作gst_rtp_jitter_buffer_loop。

    2) handle_next_buffer中会对jbuf中的item进行分析处理,如果是连续包,会依序推送至下游组件。

    3) 当jbuf中没有item,或者当包不连续的时候,会从handle_next_buffer返回GST_FLOW_WAIT,在gst_rtp_jitter_buffer_loop中会进入等待状态。

    4) 当主线程收到包,或者当一个包确认被丢弃的时候,会激活推送数据线程来进行处理。回到2) 执行。

    如图:


3. 代码片段:

3.1 函数主要入口gst_rtp_jitter_buffer_loop

    这个函数主要就是一个循环,如果可以处理包就一直处理,直到包序不连续或者jbuf空,这时候会等待包或者丢包事件的到来唤醒本线程。

  do {    // 一个循环中处理jbuf,直到处理到jbuf空或者出现包缺失,需要等待    // 这时候会等待条件变量来唤醒该线程。    result = handle_next_buffer (jitterbuffer);    if (G_LIKELY (result == GST_FLOW_WAIT)) {      /* now wait for the next event */      JBUF_WAIT_EVENT (priv, flushing);      result = GST_FLOW_OK;    }  } while (result == GST_FLOW_OK);
3.2 单个buffer处理函数 handle_next_buffer

    这个函数整体处理的流程比较清晰。

    1) 窥视jbuf的头部的item,如果为空则等待其余包到来。

    2) 根据item序号判断是否连续包(也可能是事件,在这个函数中不体现,请查看3.3),是连续包则推送至下层,重复包则直接删除,如果包序发生跳跃(即gap > 0),则说明可能出现丢包,需要等待主线程和定时器配合判断,此时也需要进行等待,等待对应包序的包(或者事件,如LOST事件)到来。

/* Peek a buffer and compare the seqnum to the expected seqnum. * If all is fine, the buffer is pushed. * If something is wrong, we wait for some event */static GstFlowReturnhandle_next_buffer (GstRtpJitterBuffer * jitterbuffer){  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;  GstFlowReturn result;  RTPJitterBufferItem *item;  guint seqnum;  guint32 next_seqnum;  /* only push buffers when PLAYING and active and not buffering */  // 状态检查  if (priv->blocked || !priv->active ||      rtp_jitter_buffer_is_buffering (priv->jbuf)) {    return GST_FLOW_WAIT;  }  /* peek a buffer, we're just looking at the sequence number.   * If all is fine, we'll pop and push it. If the sequence number is wrong we   * wait for a timeout or something to change.   * The peeked buffer is valid for as long as we hold the jitterbuffer lock. */  // 窥视jbuf第一个item,如果为空则jbuf为空,本线程进入等待。  item = rtp_jitter_buffer_peek (priv->jbuf);  if (item == NULL) {    goto wait;  }  /* get the seqnum and the next expected seqnum */  seqnum = item->seqnum;  if (seqnum == -1) {    return pop_and_push_next (jitterbuffer, seqnum);  }  next_seqnum = priv->next_seqnum;  /* get the gap between this and the previous packet. If we don't know the   * previous packet seqnum assume no gap. */  if (G_UNLIKELY (next_seqnum == -1)) {    GST_DEBUG_OBJECT (jitterbuffer, "First buffer #%d", seqnum);    /* we don't know what the next_seqnum should be, the chain function should     * have scheduled a DEADLINE timer that will increment next_seqnum when it     * fires, so wait for that */    result = GST_FLOW_WAIT;  } else {    gint gap = gst_rtp_buffer_compare_seqnum (next_seqnum, seqnum);    if (G_LIKELY (gap == 0)) {      /* no missing packet, pop and push */      // 包是预期的包,从jbuf中删除并PUSH至下游。      result = pop_and_push_next (jitterbuffer, seqnum);    } else if (G_UNLIKELY (gap < 0)) {      /* if we have a packet that we already pushed or considered dropped, pop it       * off and get the next packet */      // 重复包,从jbuf中删除并直接忽略。      GST_DEBUG_OBJECT (jitterbuffer, "Old packet #%d, next #%d dropping",          seqnum, next_seqnum);      item = rtp_jitter_buffer_pop (priv->jbuf, NULL);      free_item (item);      result = GST_FLOW_OK;    } else {      /* the chain function has scheduled timers to request retransmission or       * when to consider the packet lost, wait for that */      // 发生丢包,包序号不连续,这时候不对jbuf进行处理,返回上层进行等待,等待对应包的到来(BUFFER)或者丢失(LOST)。      GST_DEBUG_OBJECT (jitterbuffer,          "Sequence number GAP detected: expected %d instead of %d (%d missing)",          next_seqnum, seqnum, gap);      result = GST_FLOW_WAIT;    }  }  return result;wait:  {    GST_DEBUG_OBJECT (jitterbuffer, "no buffer, going to wait");    if (priv->eos) {      return GST_FLOW_EOS;    } else {      return GST_FLOW_WAIT;    }  }}
3.3 单个包的判断函数pop_and_push_next

    这个函数会对包的类型进行相应处理。

    对于BUFFER类型的包,主要是根据标志位判断其buffer上是否需要搭载额外标志位,如丢包设置DISCONT,时间偏移改变需要设置RESYNC,并根据参数ts_offset调整该buffer的pts和dts。

    LOST是一种更具体的EVENT,因此LOST处理上都是EVENT的fallthrough关系。出现LOST的时候,需要根据rtpjitterbuffer配置的do_lost参数判断是否需要将丢包的对应事件向下游发送。

    对于BUFFER类型,发送的是一个数据buffer,而对于LOST和EVENT,发送的是event。(LOST对应的event在do_lost_timeout函数中生成,具体可以参见do_lost_timeout函数)。

    对于QUERY类型,应该是pad的caps进行query或者accept的时候,这种时候的query一般是需要向下游进行传递查询。

/* take a buffer from the queue and push it */static GstFlowReturnpop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint seqnum){  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;  GstFlowReturn result = GST_FLOW_OK;  RTPJitterBufferItem *item;  GstBuffer *outbuf = NULL;  GstEvent *outevent = NULL;  GstQuery *outquery = NULL;  GstClockTime dts, pts;  gint percent = -1;  gboolean do_push = TRUE;  guint type;  GstMessage *msg;  /* when we get here we are ready to pop and push the buffer */  item = rtp_jitter_buffer_pop (priv->jbuf, &percent);  type = item->type;  switch (type) {    case ITEM_TYPE_BUFFER:      /* we need to make writable to change the flags and timestamps */      outbuf = gst_buffer_make_writable (item->data);      // 出现了丢包,包括大范围丢包或者某个seqnum被丢弃时候会置位。      // 在即将push的buffer上打上标记,并重置标志位。      if (G_UNLIKELY (priv->discont)) {        /* set DISCONT flag when we missed a packet. We pushed the buffer writable         * into the jitterbuffer so we can modify now. */        GST_DEBUG_OBJECT (jitterbuffer, "mark output buffer discont");        GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);        priv->discont = FALSE;      }      // 时间戳不同步,当调用Jitterbuffer的地方对ts_offset进行了设置      // 给buffer打上标记并重置标志位      if (G_UNLIKELY (priv->ts_discont)) {        GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC);        priv->ts_discont = FALSE;      }      // 重新设置buffer的pts和dts,结合ts_offset。      // segment暂时不是很明白具体作用。      dts =          gst_segment_position_from_running_time (&priv->segment,          GST_FORMAT_TIME, item->dts);      pts =          gst_segment_position_from_running_time (&priv->segment,          GST_FORMAT_TIME, item->pts);      /* apply timestamp with offset to buffer now */      GST_BUFFER_DTS (outbuf) = apply_offset (jitterbuffer, dts);      GST_BUFFER_PTS (outbuf) = apply_offset (jitterbuffer, pts);      /* update the elapsed time when we need to check against the npt stop time. */      update_estimated_eos (jitterbuffer, item);      priv->last_out_time = GST_BUFFER_PTS (outbuf);      break;    case ITEM_TYPE_LOST:      // 发生丢包了,置discont标志位,对应上一个case。      priv->discont = TRUE;      // 如果没有开启do_lost选项,则不会把相应的event push至下游。      if (!priv->do_lost)        do_push = FALSE;      /* FALLTHROUGH */      // 丢包时候的item数据也是一个event,具体见do_lost_timeout中的处理    case ITEM_TYPE_EVENT:      outevent = item->data;      break;    case ITEM_TYPE_QUERY:      outquery = item->data;      break;  }  /* now we are ready to push the buffer. Save the seqnum and release the lock   * so the other end can push stuff in the queue again. */  // 更新本地推送数据的记录信息  if (seqnum != -1) {    priv->last_popped_seqnum = seqnum;    priv->next_seqnum = (seqnum + item->count) & 0xffff;  }  // Buffering是否是gstreamer内部机制的buffering?或者是和rtpbin的BUFFER_MODE有关  msg = check_buffering_percent (jitterbuffer, percent);  JBUF_UNLOCK (priv);  item->data = NULL;  free_item (item);  if (msg)    gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer), msg);  switch (type) {    case ITEM_TYPE_BUFFER:      /* push buffer */      GST_DEBUG_OBJECT (jitterbuffer,          "Pushing buffer %d, dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT,          seqnum, GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)),          GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)));      // 推送至下游      result = gst_pad_push (priv->srcpad, outbuf);      JBUF_LOCK_CHECK (priv, out_flushing);      break;    case ITEM_TYPE_LOST:    case ITEM_TYPE_EVENT:      /* We got not enough consecutive packets with a huge gap, we can       * as well just drop them here now on EOS */      // 收到EOS后释放所有不连续的包。      if (GST_EVENT_TYPE (outevent) == GST_EVENT_EOS) {        GST_DEBUG_OBJECT (jitterbuffer, "Clearing gap packets on EOS");        g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL);        g_queue_clear (&priv->gap_packets);      }      GST_DEBUG_OBJECT (jitterbuffer, "%sPushing event %" GST_PTR_FORMAT          ", seqnum %d", do_push ? "" : "NOT ", outevent, seqnum);      // 推送相应事件至下游。      if (do_push)        gst_pad_push_event (priv->srcpad, outevent);      else        gst_event_unref (outevent);      result = GST_FLOW_OK;      JBUF_LOCK_CHECK (priv, out_flushing);      break;    case ITEM_TYPE_QUERY:    // 应该是pad的caps query和accept的查询,这种查询一般都要发送至下游组件。这里不展开    {      gboolean res;      res = gst_pad_peer_query (priv->srcpad, outquery);      JBUF_LOCK_CHECK (priv, out_flushing);      result = GST_FLOW_OK;      GST_LOG_OBJECT (jitterbuffer, "did query %p, return %d", outquery, res);      JBUF_SIGNAL_QUERY (priv, res);      break;    }  }  return result;  /* ERRORS */out_flushing:  {    return priv->srcresult;  }}



0 0