gst-rtsp-server实现的rtsp服务器自定义sdp

在使用gst-rtsp-server开发rtsp 服务器时,有时候在服务端收到DESCRIBE命令之后,返回给客户端的sdp,需要进行定制。

但是,gst-rtsp-server并没有在发送sdp给客户端之前,给上层应用更改sdp信息的接口。

怎么办呢?

我们先看看gst-rtsp-server的源码中,从收到客户端的命令请求,到最终发送出去sdp信息,到底是怎么做的。

下面以gst-rtsp-server-1.10.5版本为例,其它更高版本实现差异并不大。

源码中如何发送sdp

gst-rtsp-server监听以后,每一个客户端连接上来,会挂接一个GstRTSPClient。

对这个GstRtspClient的挂接过程如下:

guint  
gst_rtsp_client_attach (GstRTSPClient * client, GMainContext * context)  
{  
  GstRTSPClientPrivate *priv;  
  guint res;  
  
  g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), 0);  
  priv = client->priv;  
  g_return_val_if_fail (priv->connection != NULL, 0);  
  g_return_val_if_fail (priv->watch == NULL, 0);  
  
  /* make sure noone will free the context before the watch is destroyed */  
  priv->watch_context = g_main_context_ref (context);  
  
  /* create watch for the connection and attach */  
  priv->watch = gst_rtsp_watch_new (priv->connection, &watch_funcs,  
      g_object_ref (client), (GDestroyNotify) client_watch_notify);  
  gst_rtsp_client_set_send_func (client, do_send_message, priv->watch,  
      (GDestroyNotify) gst_rtsp_watch_unref);  
  
  gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);  
  
  GST_INFO ("client %p: attaching to context %p", client, context);  
  res = gst_rtsp_watch_attach (priv->watch, context);  
  
  return res;  
}

我们看到,在这个GstRTSPClient的Connection上,加了一个回调函数结构watch_funcs。

watch_funcs的结构定义如下:

typedef struct {  
  GstRTSPResult     (*message_received) (GstRTSPWatch *watch, GstRTSPMessage *message,  
                                         gpointer user_data);  
  GstRTSPResult     (*message_sent)     (GstRTSPWatch *watch, guint id,  
                                         gpointer user_data);  
  GstRTSPResult     (*closed)           (GstRTSPWatch *watch, gpointer user_data);  
  GstRTSPResult     (*error)            (GstRTSPWatch *watch, GstRTSPResult result,  
                                         gpointer user_data);  
  GstRTSPStatusCode (*tunnel_start)     (GstRTSPWatch *watch, gpointer user_data);  
  GstRTSPResult     (*tunnel_complete)  (GstRTSPWatch *watch, gpointer user_data);  
  GstRTSPResult     (*error_full)       (GstRTSPWatch *watch, GstRTSPResult result,  
                                         GstRTSPMessage *message, guint id,  
                                         gpointer user_data);  
  GstRTSPResult     (*tunnel_lost)      (GstRTSPWatch *watch, gpointer user_data);  
  GstRTSPResult     (*tunnel_http_response) (GstRTSPWatch *watch,  
                                             GstRTSPMessage *request,  
                                             GstRTSPMessage *response,  
                                             gpointer user_data);  
  
  /*< private >*/  
  gpointer _gst_reserved[GST_PADDING-1];  
} GstRTSPWatchFuncs;

watch_funcs的定义如下:

static GstRTSPWatchFuncs watch_funcs = {  
  message_received,  
  message_sent,  
  closed,  
  error,  
  tunnel_get,  
  tunnel_post,  
  error_full,  
  tunnel_lost,  
  tunnel_http_response  
};

我们看到,接收数据之后的处理,核心在于message_received这个函数。这个函数的实现呢,其实是调用了gst_rtsp_client_handle_message。

static GstRTSPResult  
message_received (GstRTSPWatch * watch, GstRTSPMessage * message,  
    gpointer user_data)  
{  
  return gst_rtsp_client_handle_message (GST_RTSP_CLIENT (user_data), message);  
}

gst_rtsp_client_handle_message 的实现如下:

GstRTSPResult  
gst_rtsp_client_handle_message (GstRTSPClient * client,  
    GstRTSPMessage * message)  
{  
  g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);  
  g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);  
  
  switch (message->type) {  
    case GST_RTSP_MESSAGE_REQUEST:  
      handle_request (client, message);  
      break;  
    case GST_RTSP_MESSAGE_RESPONSE:  
      handle_response (client, message);  
      break;  
    case GST_RTSP_MESSAGE_DATA:  
      handle_data (client, message);  
      break;  
    default:  
      break;  
  }  
  return GST_RTSP_OK;  
}

handle_request的开头如下:

static void  
handle_request (GstRTSPClient * client, GstRTSPMessage * request)  
{  
  GstRTSPClientPrivate *priv = client->priv;  
  GstRTSPMethod method;  
  const gchar *uristr;  
  GstRTSPUrl *uri = NULL;  
  GstRTSPVersion version;  
  GstRTSPResult res;
  ……

进入这个函数,经过认证授权等操作以后,会到达处理客户端命令的核心过程:

/* now see what is asked and dispatch to a dedicated handler */  
switch (method) {  
  case GST_RTSP_OPTIONS:  
    handle_options_request (client, ctx);  
    break;  
  case GST_RTSP_DESCRIBE:  
    handle_describe_request (client, ctx);  
    break;  
  case GST_RTSP_SETUP:  
    handle_setup_request (client, ctx);  
    break;  
  case GST_RTSP_PLAY:  
    handle_play_request (client, ctx);  
    break;  
  case GST_RTSP_PAUSE:  
    handle_pause_request (client, ctx);  
    break;  
  case GST_RTSP_TEARDOWN:  
    handle_teardown_request (client, ctx);  
    break;  
  case GST_RTSP_SET_PARAMETER:  
    handle_set_param_request (client, ctx);  
    break;  
  case GST_RTSP_GET_PARAMETER:  
    handle_get_param_request (client, ctx);  
    break;  
  case GST_RTSP_ANNOUNCE:  
    handle_announce_request (client, ctx);  
    break;  
  case GST_RTSP_RECORD:  
    handle_record_request (client, ctx);  
    break;  
  case GST_RTSP_REDIRECT:  
    if (priv->watch != NULL)  
      gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);  
    goto not_implemented;  
  case GST_RTSP_INVALID:  
  default:  
    if (priv->watch != NULL)  
      gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);  
    goto bad_request;  
}

sdp就是在handle_describe_request里返回的:

/* create an SDP for the media object on this client */  
if (!(sdp = klass->create_sdp (client, media)))  
  goto no_sdp;  
  
/* we suspend after the describe */  
gst_rtsp_media_suspend (media);  
g_object_unref (media);  
  
gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,  
    gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);  
  
gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_CONTENT_TYPE,  
    "application/sdp");  
  
/* content base for some clients that might screw up creating the setup uri */  
str = make_base_url (client, ctx->uri, path);  
g_free (path);  
  
GST_INFO ("adding content-base: %s", str);  
gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_CONTENT_BASE, str);  
  
/* add SDP to the response body */  
str = gst_sdp_message_as_text (sdp);  
gst_rtsp_message_take_body (ctx->response, (guint8 *) str, strlen (str));  
gst_sdp_message_free (sdp);  
  
send_message (client, ctx, ctx->response, FALSE);  
  
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST],  
    0, ctx);  
  
return TRUE

我们看到,sdp是通过GstRTSPClientClass的create_sdp创建的。创建出来以后,并没有任何接口再给到上层,而是通过gst_sdp_message_as_text转成一个字符串,直接附在了ctx->response结构里面,调用send_message发送给了客户端。

应用更改sdp的方法

看到这里,相信我们已经有方法了。

首先,可以自定义实现send_message方法,在send_message过程中,发现信息携带了sdp的,单独更改,然后再发送。

但是这种方法,会处理到所有的message过程,另外还需要再对已经转成字符串的信息再转回GstSdpMessage,多此一举。

所以,我们可以使用另外一种方法,即在GstRTSPClientClass的create_sdp函数里做。

具体实现的伪码大致为:

  1. 更改GstRTSPClientClass的create_sdp函数,为我们自定义的custom_create_sdp。
  2. 在我们的custom_create_sdp函数里,先调用原来的create_sdp函数,生成原来的sdp,然后进行我们的定制。
  3. 返回更改后的sdp。
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值