在使用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函数里做。
具体实现的伪码大致为:
- 更改GstRTSPClientClass的create_sdp函数,为我们自定义的custom_create_sdp。
- 在我们的custom_create_sdp函数里,先调用原来的create_sdp函数,生成原来的sdp,然后进行我们的定制。
- 返回更改后的sdp。