kmsbasertpendpoint阅读笔记

本文是关于kmsbasertpendpoint的阅读笔记,详细介绍了如何处理RTP头部扩展,包括创建、销毁和添加扩展数据。还探讨了错误纠正机制,如FEC(前向纠错)的实现,以及如何根据净荷类型码创建处理FEC的pipeline。同时,文中讨论了与统计和监控相关的函数,如设置RTP包发送的绝对时间以及配置RTP头部扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本信息

  • 阅读内容:uri-of-source-code
  • 阅读者:李北宸
  • 阅读时间:2018.07.24
  • 阅读目的:了解kmsbasertpendpoint的工作原理

阅读记录

类结构定义

struct _KmsBaseRtpEndpointPrivate
{
   
  KmsBaseRtpSession *sess;//kms定义的session,管理一个AUDIO流和一个VIDEO流

  GstElement *rtpbin;//处理rtp报文的主要element
  KmsMediaState media_state;//
  GstSDPDirection offer_dir;

  gboolean support_fec;//是否支持fec
  gboolean rtcp_mux;//是否支持rtcp_mux
  gboolean rtcp_nack;//是否支持nack
  gboolean rtcp_remb;//是否支持remb

  RtpMediaConfig *audio_config;//本地的音频流的信息,包括ssrc、是否为active流
  RtpMediaConfig *video_config;//本地的视频流的信息

  gint32 target_bitrate;
  guint min_video_recv_bw;
  guint min_video_send_bw;
  guint max_video_send_bw;

  /* Medias protected by ulpfec */
  KmsList *prot_medias;//使用fec的媒体

  /* REMB */
  GstStructure *remb_params;//配置remb的参数
  KmsRembLocal *rl;//管理本地REMB的数据结构,具体可以参考kmsremb.md
  KmsRembRemote *rm;//管理对端REMB的数据结构

  /* Port range */
  guint min_port;
  guint max_port;

  /* RTP statistics */
  KmsBaseRTPStats stats;//一些统计信息

  /* Timestamps */
  gssize init_stats;
  FILE *stats_file;//记录统计信息的文件指针

  /* Synchronization */
  KmsRtpSynchronizer *sync_audio;//设置音频流pts的synchronizer
  KmsRtpSynchronizer *sync_video;//设置视频流pts的synchronizer
  gboolean perform_video_sync;//似乎是指是否要让音频流和视频流之间同步
};
typedef struct _HdrExtData
{
   
  GstPad *pad;
  /* Useful to make buffers writable when needed. */
  gboolean add_hdr;
  gboolean set_time;
  gint abs_send_time_id;
} HdrExtData;

当一个RTP报文头部的x位为1时,表明RTP报文头部后边会有一个头部扩展。RTP协议并不要求任何头部扩展,只有上层协议有需要时才会添加头部扩展。

HdrExtData是为了方便给RTP报文添加扩展头而写的struct。

pad:对流经哪个pad的RTP包添加扩展头。

add_hdr:是否需要给流经pad的RTP包添加扩展头。

set_time:如果RTP包中已经有了绝对时间扩展头,那是否要更新它。

abs_send_time_id:需要添加扩展头的媒体的净荷类型码。

typedef struct _ExtData
{
   
  KmsRefStruct ref;
  gint ulpfec_pt;
  gint red_pt;
} ExtData;

ref:相当以这个struct的ref count,用来释放内存,对算法没什么意义。

ulpfec_pt:创建ulpfecdec、ulpfecend时使用的净荷类型码。

red_pt:创建reddec、redenc时使用的净荷类型码。

函数功能

static gboolean is_proto (const gchar * term, const gchar * opt, const gchar * proto)

term是否匹配正则表达式“(opt)?proto”,用来判断term是否为一个表示protocal的字符串。

static HdrExtData *hdr_ext_data_new (GstPad * pad, gboolean add_hdr, gboolean set_time,gint abs_send_time_id)

创建一个HdrExtData,用输入的参数给它赋值。

static void hdr_ext_data_destroy (HdrExtData * data)

释放一个HdrExtData类型的对象data。

static void hdr_ext_data_destroy_pointer (gpointer data)

调用hdr_ext_data_destroy,利用指针data释放其对应的HdrExtData对象。

static void kms_base_rtp_endpoint_rtp_hdr_ext_set_time (guint8 * data)

获得绝对时间(ms),把它转换成大端模式存在data中。

static void kms_base_rtp_endpoint_add_rtp_hdr_ext (HdrExtData * data, GstBuffer * buffer)

主要作用:向一个RTP报文buffer中添加data所代表的扩展头,这个扩展头具体内容为一个包发送的绝对时间(abs_send_time)。

  if (data->add_hdr) {
   
    map_flags = GST_MAP_WRITE;
  } else {
   
    map_flags = GST_MAP_READ;
  }

data->add_hdr表示是否要向RTP增加扩展头部,要加入扩展头就要以写方式打开。

  if (!gst_rtp_buffer_map (buffer, map_flags, &rtp)) {
   
    GST_WARNING_OBJECT (data->pad, "Can not map RTP buffer");
    return;
  }

把buffer作为rtp包映射到rtp上。

  if (!gst_rtp_buffer_get_extension_onebyte_header (&rtp,
          id, 0, (gpointer) & time, &size)) {
   
    GST_TRACE_OBJECT (data->pad,
        "RTP hdrext abs-send-time with id '%d' not found", id);

根据RFC5285的规定,RTP扩展头部有两种格式:one byte header和two byte header。

one byte header的格式:4 bit的id(就是净荷类型码),4 bit的length(byte),之后是length长的数据部分。

gst_rtp_buffer_get_extension_onebyte_header函数的参数“id”和“0”表示寻找rtp中第一个使用id的头部扩展,把它的数据存在time中,它的大小为size(byte)。

如果这个函数返回FALSE说明没有这个扩展头部。

    if (data->add_hdr) {
   //需要添加头部扩展
      GST_TRACE_OBJECT (data->pad, " Adding new one.");
    } else {
   
      GST_WARNING_OBJECT (data->pad, "Cannot add new one: not writable");
      goto end;
    }

    time = g_malloc0 (RTP_HDR_EXT_ABS_SEND_TIME_SIZE);
    if (data->set_time) {
   //需要设定time
      kms_base_rtp_endpoint_rtp_hdr_ext_set_time (time);
    }

    if (!gst_rtp_buffer_add_extension_onebyte_header (&rtp,
            id, time, RTP_HDR_EXT_ABS_SEND_TIME_SIZE)) {
   //给rtp加入头部扩展
      GST_WARNING_OBJECT (data->pad, "RTP hdrext abs-send-time not added");
    }

    g_free (time);

没有对应的头部扩展时,根据data的参数决定是否加入头部扩展。

else if (data->set_time) {
   
    if (size != RTP_HDR_EXT_ABS_SEND_TIME_SIZE) {
   //size和time类头部扩展的大小不匹配
      GST_WARNING_OBJECT (data->pad,
          "RTP hdrext abs-send-time size with id '%d' not matching", id);
    } else {
   //更新头部扩展
      GST_TRACE_OBJECT (data->pad,
          "RTP hdrext abs-send-time with id '%d' found. Update time.", id);
      kms_base_rtp_endpoint_rtp_hdr_ext_set_time (time);
    }
  }

在rtp中已经有对应的头部扩展。

static gboolean kms_base_rtp_endpoint_add_rtp_hdr_ext_bufflist (GstBuffer ** buf, guint idx, HdrExtData * data)

对buffer list的第一个元素*buf调用kms_base_rtp_endpoint_add_rtp_hdr_ext添加扩展头。

static GstPadProbeReturn kms_base_rtp_endpoint_add_rtp_hdr_ext_probe (GstPad * pad, GstPadProbeInfo * info, gpointer gp)

主要作用:给buffer添加扩展头的probe。

  HdrExtData *data = (HdrExtData *) gp;

gp实际上就是一个HdrExtData。

  if (GST_PAD_PROBE_INFO_TYPE (info) & GST_PAD_PROBE_TYPE_BUFFER) {
   //buffer
    GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);

    if (data->add_hdr) {
   
      buffer = gst_buffer_make_writable (buffer);
    }
    kms_base_rtp_endpoint_add_rtp_hdr_ext (data, buffer);
    GST_PAD_PROBE_INFO_DATA (info) = buffer;
  }

如果data->add_hdr为TRUE,调用kms_base_rtp_endpoint_add_rtp_hdr_ext添加扩展头。

else if (GST_PAD_PROBE_INFO_TYPE (info) & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
   //buffer list
    GstBufferList *bufflist = GST_PAD_PROBE_INFO_BUFFER_LIST (info);

    if (data->add_hdr) {
   
      bufflist = gst_buffer_list_make_writable (bufflist);
    }
    gst_buffer_list_foreach (bufflist,
        (GstBufferListFunc) kms_base_rtp_endpoint_add_rtp_hdr_ext_bufflist,
        data);

    GST_PAD_PROBE_INFO_DATA (info) = bufflist;
  }

对buffer list中的每个buffer,调用kms_base_rtp_endpoint_add_rtp_hdr_ext_bufflist添加扩展头。

static void kms_base_rtp_endpoint_config_rtp_hdr_ext (KmsBaseRtpEndpoint * self, const GstSDPMedia * media, GstElement * payloader)

主要作用:向payloader的一个src pad上添加probe,使得经过这个pad的所有RTP包打上一个扩展头部,内容为这个包发送的绝对时间。


取media的净荷类型码。扩展头部的“id”需要使用这个净荷类型码。

  pad = gst_element_get_static_pad (payloader, "src");
  if (pad == NULL) {
   
    GST_WARNING_OBJECT (self, "No RTP pad to configure hdrext probe.");
    return;
  }

取payloader的src pad。

payloader是给RTP报文添加净荷类型码的element。

  data = hdr_ext_data_new (pad, TRUE, FALSE, abs_send_time_id);
  gst_pad_add_probe (pad,
      GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
      kms_base_rtp_endpoint_add_rtp_hdr_ext_probe, data,
      hdr_ext_data_destroy_pointer);

创建一个HdrExtData,设定自动给流经此pad的RTP包添加扩展头的probe。

static GstSDPDirection on_offer_media_direction (KmsSdpMediaDirectionExt * ext, KmsBaseRtpEndpoint * self)

取self的offer_dir属性,返回。

这个函数的意义就是,需要提供media direction时,提供自己的direction。

on_answer_media_direction (KmsSdpMediaDirectionExt * ext, GstSDPDirection dir, KmsBaseRtpEndpoint * self)

获得和dir相对的方向,返回。

这个函数的意义就是,对端给出了offer,我们要给出和offer的direction相对的direction。

相对的方向:

Sendonly对应Recvonly,SendRecv对应SendRecv,Inactive对应Inactive。

static gboolean on_offered_ulp_fec_cb (KmsSdpUlpFecExt * ext, guint pt, guint clock_rate, gpointer user_data)

user_data实际上是ExtData类型的。

设置user_data->ulpfec_pt为pt。

返回值为TRUE。(重复调用)

static gboolean on_offered_redundancy_cb (KmsSdpUlpFecExt * ext, guint pt, guint clock_rate, gpointer user_data)

user_data是一个ExtData。

设置user_data->red_pt为pt。

static void kms_base_rtp_configure_extensions (KmsBaseRtpEndpoint * self, const gchar * media, KmsSdpMediaHandler * handler)

主要作用:给media创建一个direction ext,加入handler中;如果支持前向纠错(self->priv->support_fec == TURE),还要创建ulpfec ext和red ext,加入handler中。

简单来说,就是给meida的handler添加extension。

  mediadirext = kms_sdp_media_direction_ext_new ();

  g_signal_connect (mediadirext, "on-offer-media-direction",
      G_CALLBACK (on_offer_media_direction), self);
  g_signal_connect (mediadirext, "on-answer-media-direction",
      G_CALLBACK (on_answer_media_direction), self);

创建一个KmsSdpMediaDirectionExt,给他的信号绑定回调函数,根据收到的信号自动判断本地媒体描述的direction。

  kms_sdp_media_handler_add_media_extension (handler,
      KMS_I_SDP_MEDIA_EXTENSION (mediadirext));

把mediadirext加入handler->priv->extensions中。

  if (!self->priv->support_fec) {
   
    return;
  }

判断是否支持前向纠错。后边都是对fec的配置。

  edata = ext_data_new ();
  kms_list_append (self->priv->prot_medias, g_strdup (media), edata);

创建一个ExtData,把它以<g_strdup (media),edata>(<索引,值>)插入self->priv->prot_medias(一个KmsList,保存所有使用fec的miedia)中。

  ulpfecext = kms_sdp_ulp_fec_ext_new ();
  redext = kms_sdp_redundant_ext_new ();

  g_signal_connect_data (redext, "on-offered-redundancy",
      G_CALLBACK (on_offered_redundancy_cb),
      kms_ref_struct_ref (KMS_REF_STRUCT_CAST (edata)),
      (GClosureNotify) kms_ref_struct_unref, 0);
  g_signal_connect_data (ulpfecext, "on-offered-ulp-fec",
      G_CALLBACK (on_offered_ulp_fec_cb),
      kms_ref_struct_ref (KMS_REF_STRUCT_CAST (edata)),
      (GClosureNotify) kms_ref_struct_unref, 0);

创建两个ext,分别给他们的信号绑定回调函数,在收到信号时分别设置edata->red_pt和edata->ulpfec_pt。


把两个ext加入handler。

static void kms_base_rtp_create_media_handler (KmsBaseSdpEndpoint * base_sdp, const gchar * media, KmsSdpMediaHandler ** handler)

主要作用:给handler的rtcp-mu、nack、goog-remb赋值,并配置它的direction ext、ulpfec ext、red ext。

  g_object_set (G_OBJECT (*handler), "rtcp-mux", self->priv->rtcp_mux, NULL);

给handler的属性rtcp-mux赋值为self->priv->rtcp_mux。

  if (KMS_IS_SDP_RTP_AVPF_MEDIA_HANDLER (*handler)) {
   
    g_object_set (G_OBJECT (*handler),
        "nack", self->priv->rtcp_nack,
        "goog-remb", self->priv->rtcp_remb, NULL);
  }

如果handler支持nack和remb,这两个也同样赋值。

  h_avp = KMS_SDP_RTP_AVP_MEDIA_HANDLER (*handler);
  kms_sdp_rtp_avp_media_handler_add_extmap (h_avp, RTP_HDR_EXT_ABS_SEND_TIME_ID,
      RTP_HDR_EXT_ABS_SEND_TIME_URI, &err);

把<RTP_HDR_EXT_ABS_SEND_TIME_ID,RTP_HDR_EXT_ABS_SEND_TIME_URI>插入handler->extmap(一个GHash)。

RTP_HDR_EXT_ABS_SEND_TIME_ID是3;RTP_HDR_EXT_ABS_SEND_TIME_URI是“http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time”

  kms_base_rtp_configure_extensions (self, media, *handler);

调用上一个函数kms_base_rtp_configure_extensions,配置direction ext、ulpfec ext、red ext。

static ConnectPayloaderData *connect_payloader_data_new (KmsBaseRtpEndpoint * self, GstElement * payloader, KmsElementPadType type)

创建一个ConnectPayloaderData data,并给它的一些域赋值:data->self = self、data->payloader = payloader、data->type = type。

static KmsSSRCStats *ssrc_stats_new (guint ssrc, GstElement * jitter_buffer)

创建一个KmsSSRCStats stats,并赋值:stats->jitter_buffer = gst_object_ref (jitter_buffer)、stats->ssrc = ssrc。

static KmsRTPSessionStats *rtp_session_stats_new (GObject * rtp_session, GstSDPDirection direction)

创建一个KmsRTPSessionStats,并给它的rtp_session、direction域赋值。

static gboolean kms_base_rtp_endpoint_is_video_rtcp_nack (KmsBaseRtpEndpoint * self)

主要作用:判断视频流是否使用了nack。

  KmsBaseSdpEndpoint *base_endpoint = KMS_BASE_SDP_ENDPOINT (self);
  const GstSDPMessage *sdp =
      kms_base_sdp_endpoint_get_first_negotiated_sdp (base_endpoint);

取base_endpoint->first_neg_sdp。这是这个endpoint的会话描述。

  for (i = 0; i < len; i++) {
   
    const GstSDPMedia *media = gst_sdp_message_get_media (sdp, i);
    const gchar *media_str = gst_sdp_media_get_media (media);

    if (g_strcmp0 (VIDEO_STREAM_NAME, media_str) == 0) {
   
      return sdp_utils_media_has_rtcp_nack (media);
    }
  }

遍历会话描述的所有媒体描述(其实作用是找到media_str为“video”的媒体描述,即视频流)。判断它是否使用了nack。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值