2 * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 * @short_description: A client connection state
22 * @see_also: #GstRTSPServer, #GstRTSPThreadPool
24 * The client object handles the connection with a client for as long as a TCP
27 * A #GstRTSPClient is created by #GstRTSPServer when a new connection is
28 * accepted and it inherits the #GstRTSPMountPoints, #GstRTSPSessionPool,
29 * #GstRTSPAuth and #GstRTSPThreadPool from the server.
31 * The client connection should be configured with the #GstRTSPConnection using
32 * gst_rtsp_client_set_connection() before it can be attached to a #GMainContext
33 * using gst_rtsp_client_attach(). From then on the client will handle requests
36 * Use gst_rtsp_client_session_filter() to iterate or modify all the
37 * #GstRTSPSession objects managed by the client object.
39 * Last reviewed on 2013-07-11 (1.0.0)
45 #include <gst/sdp/gstmikey.h>
47 #include "rtsp-client.h"
49 #include "rtsp-params.h"
51 #define GST_RTSP_CLIENT_GET_PRIVATE(obj) \
52 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_CLIENT, GstRTSPClientPrivate))
55 * send_lock, lock, tunnels_lock
58 struct _GstRTSPClientPrivate
60 GMutex lock; /* protects everything else */
62 GstRTSPConnection *connection;
64 GMainContext *watch_context;
69 GstRTSPClientSendFunc send_func; /* protected by send_lock */
70 gpointer send_data; /* protected by send_lock */
71 GDestroyNotify send_notify; /* protected by send_lock */
73 GstRTSPSessionPool *session_pool;
74 gulong session_removed_id;
75 GstRTSPMountPoints *mount_points;
77 GstRTSPThreadPool *thread_pool;
79 /* used to cache the media in the last requested DESCRIBE so that
80 * we can pick it up in the next SETUP immediately */
86 guint sessions_cookie;
88 gboolean drop_backlog;
91 static GMutex tunnels_lock;
92 static GHashTable *tunnels; /* protected by tunnels_lock */
94 #define DEFAULT_SESSION_POOL NULL
95 #define DEFAULT_MOUNT_POINTS NULL
96 #define DEFAULT_DROP_BACKLOG TRUE
111 SIGNAL_OPTIONS_REQUEST,
112 SIGNAL_DESCRIBE_REQUEST,
113 SIGNAL_SETUP_REQUEST,
115 SIGNAL_PAUSE_REQUEST,
116 SIGNAL_TEARDOWN_REQUEST,
117 SIGNAL_SET_PARAMETER_REQUEST,
118 SIGNAL_GET_PARAMETER_REQUEST,
119 SIGNAL_HANDLE_RESPONSE,
124 GST_DEBUG_CATEGORY_STATIC (rtsp_client_debug);
125 #define GST_CAT_DEFAULT rtsp_client_debug
127 static guint gst_rtsp_client_signals[SIGNAL_LAST] = { 0 };
129 static void gst_rtsp_client_get_property (GObject * object, guint propid,
130 GValue * value, GParamSpec * pspec);
131 static void gst_rtsp_client_set_property (GObject * object, guint propid,
132 const GValue * value, GParamSpec * pspec);
133 static void gst_rtsp_client_finalize (GObject * obj);
135 static GstSDPMessage *create_sdp (GstRTSPClient * client, GstRTSPMedia * media);
136 static void unlink_session_transports (GstRTSPClient * client,
137 GstRTSPSession * session, GstRTSPSessionMedia * sessmedia);
138 static gboolean default_configure_client_media (GstRTSPClient * client,
139 GstRTSPMedia * media, GstRTSPStream * stream, GstRTSPContext * ctx);
140 static gboolean default_configure_client_transport (GstRTSPClient * client,
141 GstRTSPContext * ctx, GstRTSPTransport * ct);
142 static GstRTSPResult default_params_set (GstRTSPClient * client,
143 GstRTSPContext * ctx);
144 static GstRTSPResult default_params_get (GstRTSPClient * client,
145 GstRTSPContext * ctx);
146 static gchar *default_make_path_from_uri (GstRTSPClient * client,
147 const GstRTSPUrl * uri);
148 static gboolean default_handle_options_request (GstRTSPClient * client,
149 GstRTSPContext * ctx);
150 static gboolean default_handle_set_param_request (GstRTSPClient * client,
151 GstRTSPContext * ctx);
152 static gboolean default_handle_get_param_request (GstRTSPClient * client,
153 GstRTSPContext * ctx);
154 static void client_session_removed (GstRTSPSessionPool * pool,
155 GstRTSPSession * session, GstRTSPClient * client);
157 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
160 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
162 GObjectClass *gobject_class;
164 g_type_class_add_private (klass, sizeof (GstRTSPClientPrivate));
166 gobject_class = G_OBJECT_CLASS (klass);
168 gobject_class->get_property = gst_rtsp_client_get_property;
169 gobject_class->set_property = gst_rtsp_client_set_property;
170 gobject_class->finalize = gst_rtsp_client_finalize;
172 klass->create_sdp = create_sdp;
173 klass->configure_client_media = default_configure_client_media;
174 klass->configure_client_transport = default_configure_client_transport;
175 klass->params_set = default_params_set;
176 klass->params_get = default_params_get;
177 klass->make_path_from_uri = default_make_path_from_uri;
178 klass->handle_options_request = default_handle_options_request;
179 klass->handle_set_param_request = default_handle_set_param_request;
180 klass->handle_get_param_request = default_handle_get_param_request;
182 g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
183 g_param_spec_object ("session-pool", "Session Pool",
184 "The session pool to use for client session",
185 GST_TYPE_RTSP_SESSION_POOL,
186 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
188 g_object_class_install_property (gobject_class, PROP_MOUNT_POINTS,
189 g_param_spec_object ("mount-points", "Mount Points",
190 "The mount points to use for client session",
191 GST_TYPE_RTSP_MOUNT_POINTS,
192 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
194 g_object_class_install_property (gobject_class, PROP_DROP_BACKLOG,
195 g_param_spec_boolean ("drop-backlog", "Drop Backlog",
196 "Drop data when the backlog queue is full",
197 DEFAULT_DROP_BACKLOG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
199 gst_rtsp_client_signals[SIGNAL_CLOSED] =
200 g_signal_new ("closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
201 G_STRUCT_OFFSET (GstRTSPClientClass, closed), NULL, NULL,
202 g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
204 gst_rtsp_client_signals[SIGNAL_NEW_SESSION] =
205 g_signal_new ("new-session", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
206 G_STRUCT_OFFSET (GstRTSPClientClass, new_session), NULL, NULL,
207 g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_RTSP_SESSION);
209 gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST] =
210 g_signal_new ("options-request", G_TYPE_FROM_CLASS (klass),
211 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, options_request),
212 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
213 GST_TYPE_RTSP_CONTEXT);
215 gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST] =
216 g_signal_new ("describe-request", G_TYPE_FROM_CLASS (klass),
217 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, describe_request),
218 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
219 GST_TYPE_RTSP_CONTEXT);
221 gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST] =
222 g_signal_new ("setup-request", G_TYPE_FROM_CLASS (klass),
223 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, setup_request),
224 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
225 GST_TYPE_RTSP_CONTEXT);
227 gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST] =
228 g_signal_new ("play-request", G_TYPE_FROM_CLASS (klass),
229 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, play_request),
230 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
231 GST_TYPE_RTSP_CONTEXT);
233 gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST] =
234 g_signal_new ("pause-request", G_TYPE_FROM_CLASS (klass),
235 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, pause_request),
236 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
237 GST_TYPE_RTSP_CONTEXT);
239 gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST] =
240 g_signal_new ("teardown-request", G_TYPE_FROM_CLASS (klass),
241 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, teardown_request),
242 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
243 GST_TYPE_RTSP_CONTEXT);
245 gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST] =
246 g_signal_new ("set-parameter-request", G_TYPE_FROM_CLASS (klass),
247 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
248 set_parameter_request), NULL, NULL, g_cclosure_marshal_generic,
249 G_TYPE_NONE, 1, GST_TYPE_RTSP_CONTEXT);
251 gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST] =
252 g_signal_new ("get-parameter-request", G_TYPE_FROM_CLASS (klass),
253 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
254 get_parameter_request), NULL, NULL, g_cclosure_marshal_generic,
255 G_TYPE_NONE, 1, GST_TYPE_RTSP_CONTEXT);
257 gst_rtsp_client_signals[SIGNAL_HANDLE_RESPONSE] =
258 g_signal_new ("handle-response", G_TYPE_FROM_CLASS (klass),
259 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
260 handle_response), NULL, NULL, g_cclosure_marshal_generic,
261 G_TYPE_NONE, 1, GST_TYPE_RTSP_CONTEXT);
264 * GstRTSPClient::send-message:
265 * @client: The RTSP client
266 * @session: (type GstRtspServer.RTSPSession): The session
267 * @message: (type GstRtsp.RTSPMessage): The message
269 gst_rtsp_client_signals[SIGNAL_SEND_MESSAGE] =
270 g_signal_new ("send-message", G_TYPE_FROM_CLASS (klass),
271 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
272 G_TYPE_NONE, 2, GST_TYPE_RTSP_CONTEXT, G_TYPE_POINTER);
275 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
276 g_mutex_init (&tunnels_lock);
278 GST_DEBUG_CATEGORY_INIT (rtsp_client_debug, "rtspclient", 0, "GstRTSPClient");
282 gst_rtsp_client_init (GstRTSPClient * client)
284 GstRTSPClientPrivate *priv = GST_RTSP_CLIENT_GET_PRIVATE (client);
288 g_mutex_init (&priv->lock);
289 g_mutex_init (&priv->send_lock);
291 priv->drop_backlog = DEFAULT_DROP_BACKLOG;
294 static GstRTSPFilterResult
295 filter_session_media (GstRTSPSession * sess, GstRTSPSessionMedia * sessmedia,
298 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
300 gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
301 unlink_session_transports (client, sess, sessmedia);
303 /* unmanage the media in the session */
304 return GST_RTSP_FILTER_REMOVE;
308 client_watch_session (GstRTSPClient * client, GstRTSPSession * session)
310 GstRTSPClientPrivate *priv = client->priv;
312 g_mutex_lock (&priv->lock);
313 /* check if we already know about this session */
314 if (g_list_find (priv->sessions, session) == NULL) {
315 GST_INFO ("watching session %p", session);
317 priv->sessions = g_list_prepend (priv->sessions, g_object_ref (session));
318 priv->sessions_cookie++;
320 /* connect removed session handler, it will be disconnected when the last
321 * session gets removed */
322 if (priv->session_removed_id == 0)
323 priv->session_removed_id = g_signal_connect_data (priv->session_pool,
324 "session-removed", G_CALLBACK (client_session_removed),
325 g_object_ref (client), (GClosureNotify) g_object_unref, 0);
327 g_mutex_unlock (&priv->lock);
332 /* should be called with lock */
334 client_unwatch_session (GstRTSPClient * client, GstRTSPSession * session,
337 GstRTSPClientPrivate *priv = client->priv;
339 GST_INFO ("client %p: unwatch session %p", client, session);
342 link = g_list_find (priv->sessions, session);
347 priv->sessions = g_list_delete_link (priv->sessions, link);
348 priv->sessions_cookie++;
350 /* if this was the last session, disconnect the handler.
351 * This will also drop the extra client ref */
352 if (!priv->sessions) {
353 g_signal_handler_disconnect (priv->session_pool, priv->session_removed_id);
354 priv->session_removed_id = 0;
357 /* unlink all media managed in this session */
358 gst_rtsp_session_filter (session, filter_session_media, client);
360 /* remove the session */
361 g_object_unref (session);
364 static GstRTSPFilterResult
365 cleanup_session (GstRTSPClient * client, GstRTSPSession * sess,
368 return GST_RTSP_FILTER_REMOVE;
371 /* A client is finalized when the connection is broken */
373 gst_rtsp_client_finalize (GObject * obj)
375 GstRTSPClient *client = GST_RTSP_CLIENT (obj);
376 GstRTSPClientPrivate *priv = client->priv;
378 GST_INFO ("finalize client %p", client);
381 gst_rtsp_watch_set_flushing (priv->watch, TRUE);
382 gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
385 g_source_destroy ((GSource *) priv->watch);
387 if (priv->watch_context)
388 g_main_context_unref (priv->watch_context);
390 /* all sessions should have been removed by now. We keep a ref to
391 * the client object for the session removed handler. The ref is
392 * dropped when the last session is removed from the list. */
393 g_assert (priv->sessions == NULL);
394 g_assert (priv->session_removed_id == 0);
396 if (priv->connection)
397 gst_rtsp_connection_free (priv->connection);
398 if (priv->session_pool) {
399 g_object_unref (priv->session_pool);
401 if (priv->mount_points)
402 g_object_unref (priv->mount_points);
404 g_object_unref (priv->auth);
405 if (priv->thread_pool)
406 g_object_unref (priv->thread_pool);
411 gst_rtsp_media_unprepare (priv->media);
412 g_object_unref (priv->media);
415 g_free (priv->server_ip);
416 g_mutex_clear (&priv->lock);
417 g_mutex_clear (&priv->send_lock);
419 G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
423 gst_rtsp_client_get_property (GObject * object, guint propid,
424 GValue * value, GParamSpec * pspec)
426 GstRTSPClient *client = GST_RTSP_CLIENT (object);
427 GstRTSPClientPrivate *priv = client->priv;
430 case PROP_SESSION_POOL:
431 g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
433 case PROP_MOUNT_POINTS:
434 g_value_take_object (value, gst_rtsp_client_get_mount_points (client));
436 case PROP_DROP_BACKLOG:
437 g_value_set_boolean (value, priv->drop_backlog);
440 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
445 gst_rtsp_client_set_property (GObject * object, guint propid,
446 const GValue * value, GParamSpec * pspec)
448 GstRTSPClient *client = GST_RTSP_CLIENT (object);
449 GstRTSPClientPrivate *priv = client->priv;
452 case PROP_SESSION_POOL:
453 gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
455 case PROP_MOUNT_POINTS:
456 gst_rtsp_client_set_mount_points (client, g_value_get_object (value));
458 case PROP_DROP_BACKLOG:
459 g_mutex_lock (&priv->lock);
460 priv->drop_backlog = g_value_get_boolean (value);
461 g_mutex_unlock (&priv->lock);
464 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
469 * gst_rtsp_client_new:
471 * Create a new #GstRTSPClient instance.
473 * Returns: (transfer full): a new #GstRTSPClient
476 gst_rtsp_client_new (void)
478 GstRTSPClient *result;
480 result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
486 send_message (GstRTSPClient * client, GstRTSPContext * ctx,
487 GstRTSPMessage * message, gboolean close)
489 GstRTSPClientPrivate *priv = client->priv;
491 gst_rtsp_message_add_header (message, GST_RTSP_HDR_SERVER,
492 "GStreamer RTSP server");
494 /* remove any previous header */
495 gst_rtsp_message_remove_header (message, GST_RTSP_HDR_SESSION, -1);
497 /* add the new session header for new session ids */
499 gst_rtsp_message_take_header (message, GST_RTSP_HDR_SESSION,
500 gst_rtsp_session_get_header (ctx->session));
503 if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
504 gst_rtsp_message_dump (message);
508 gst_rtsp_message_add_header (message, GST_RTSP_HDR_CONNECTION, "close");
510 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SEND_MESSAGE],
513 g_mutex_lock (&priv->send_lock);
515 priv->send_func (client, message, close, priv->send_data);
516 g_mutex_unlock (&priv->send_lock);
518 gst_rtsp_message_unset (message);
522 send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
523 GstRTSPContext * ctx)
525 gst_rtsp_message_init_response (ctx->response, code,
526 gst_rtsp_status_as_text (code), ctx->request);
530 send_message (client, ctx, ctx->response, FALSE);
534 send_option_not_supported_response (GstRTSPClient * client,
535 GstRTSPContext * ctx, const gchar * unsupported_options)
537 GstRTSPStatusCode code = GST_RTSP_STS_OPTION_NOT_SUPPORTED;
539 gst_rtsp_message_init_response (ctx->response, code,
540 gst_rtsp_status_as_text (code), ctx->request);
542 if (unsupported_options != NULL) {
543 gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_UNSUPPORTED,
544 unsupported_options);
549 send_message (client, ctx, ctx->response, FALSE);
553 paths_are_equal (const gchar * path1, const gchar * path2, gint len2)
555 if (path1 == NULL || path2 == NULL)
558 if (strlen (path1) != len2)
561 if (strncmp (path1, path2, len2))
567 /* this function is called to initially find the media for the DESCRIBE request
568 * but is cached for when the same client (without breaking the connection) is
569 * doing a setup for the exact same url. */
570 static GstRTSPMedia *
571 find_media (GstRTSPClient * client, GstRTSPContext * ctx, gchar * path,
574 GstRTSPClientPrivate *priv = client->priv;
575 GstRTSPMediaFactory *factory;
579 /* find the longest matching factory for the uri first */
580 if (!(factory = gst_rtsp_mount_points_match (priv->mount_points,
584 ctx->factory = factory;
586 if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_ACCESS))
587 goto no_factory_access;
589 if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_CONSTRUCT))
595 path_len = strlen (path);
597 if (!paths_are_equal (priv->path, path, path_len)) {
598 GstRTSPThread *thread;
600 /* remove any previously cached values before we try to construct a new
606 gst_rtsp_media_unprepare (priv->media);
607 g_object_unref (priv->media);
611 /* prepare the media and add it to the pipeline */
612 if (!(media = gst_rtsp_media_factory_construct (factory, ctx->uri)))
617 thread = gst_rtsp_thread_pool_get_thread (priv->thread_pool,
618 GST_RTSP_THREAD_TYPE_MEDIA, ctx);
622 /* prepare the media */
623 if (!(gst_rtsp_media_prepare (media, thread)))
626 /* now keep track of the uri and the media */
627 priv->path = g_strndup (path, path_len);
630 /* we have seen this path before, used cached media */
633 GST_INFO ("reusing cached media %p for path %s", media, priv->path);
636 g_object_unref (factory);
640 g_object_ref (media);
647 GST_ERROR ("client %p: no factory for path %s", client, path);
648 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
653 GST_ERROR ("client %p: not authorized to see factory path %s", client,
655 /* error reply is already sent */
660 GST_ERROR ("client %p: not authorized for factory path %s", client, path);
661 /* error reply is already sent */
666 GST_ERROR ("client %p: can't create media", client);
667 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
668 g_object_unref (factory);
674 GST_ERROR ("client %p: can't create thread", client);
675 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
676 g_object_unref (media);
678 g_object_unref (factory);
684 GST_ERROR ("client %p: can't prepare media", client);
685 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
686 g_object_unref (media);
688 g_object_unref (factory);
695 do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
697 GstRTSPClientPrivate *priv = client->priv;
698 GstRTSPMessage message = { 0 };
703 gst_rtsp_message_init_data (&message, channel);
705 /* FIXME, need some sort of iovec RTSPMessage here */
706 if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ))
709 gst_rtsp_message_take_body (&message, map_info.data, map_info.size);
711 g_mutex_lock (&priv->send_lock);
713 priv->send_func (client, &message, FALSE, priv->send_data);
714 g_mutex_unlock (&priv->send_lock);
716 gst_rtsp_message_steal_body (&message, &data, &usize);
717 gst_buffer_unmap (buffer, &map_info);
719 gst_rtsp_message_unset (&message);
725 link_transport (GstRTSPClient * client, GstRTSPSession * session,
726 GstRTSPStreamTransport * trans)
728 GstRTSPClientPrivate *priv = client->priv;
730 GST_DEBUG ("client %p: linking transport %p", client, trans);
732 gst_rtsp_stream_transport_set_callbacks (trans,
733 (GstRTSPSendFunc) do_send_data,
734 (GstRTSPSendFunc) do_send_data, client, NULL);
736 priv->transports = g_list_prepend (priv->transports, trans);
738 /* make sure our session can't expire */
739 gst_rtsp_session_prevent_expire (session);
743 link_session_transports (GstRTSPClient * client, GstRTSPSession * session,
744 GstRTSPSessionMedia * sessmedia)
749 gst_rtsp_media_n_streams (gst_rtsp_session_media_get_media (sessmedia));
750 for (i = 0; i < n_streams; i++) {
751 GstRTSPStreamTransport *trans;
752 const GstRTSPTransport *tr;
754 /* get the transport, if there is no transport configured, skip this stream */
755 trans = gst_rtsp_session_media_get_transport (sessmedia, i);
759 tr = gst_rtsp_stream_transport_get_transport (trans);
761 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
762 /* for TCP, link the stream to the TCP connection of the client */
763 link_transport (client, session, trans);
769 unlink_transport (GstRTSPClient * client, GstRTSPSession * session,
770 GstRTSPStreamTransport * trans)
772 GstRTSPClientPrivate *priv = client->priv;
774 GST_DEBUG ("client %p: unlinking transport %p", client, trans);
776 gst_rtsp_stream_transport_set_callbacks (trans, NULL, NULL, NULL, NULL);
778 priv->transports = g_list_remove (priv->transports, trans);
780 /* our session can now expire */
781 gst_rtsp_session_allow_expire (session);
785 unlink_session_transports (GstRTSPClient * client, GstRTSPSession * session,
786 GstRTSPSessionMedia * sessmedia)
791 gst_rtsp_media_n_streams (gst_rtsp_session_media_get_media (sessmedia));
792 for (i = 0; i < n_streams; i++) {
793 GstRTSPStreamTransport *trans;
794 const GstRTSPTransport *tr;
796 /* get the transport, if there is no transport configured, skip this stream */
797 trans = gst_rtsp_session_media_get_transport (sessmedia, i);
801 tr = gst_rtsp_stream_transport_get_transport (trans);
803 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
804 /* for TCP, unlink the stream from the TCP connection of the client */
805 unlink_transport (client, session, trans);
811 * gst_rtsp_client_close:
812 * @client: a #GstRTSPClient
814 * Close the connection of @client and remove all media it was managing.
819 gst_rtsp_client_close (GstRTSPClient * client)
821 GstRTSPClientPrivate *priv = client->priv;
822 const gchar *tunnelid;
824 GST_DEBUG ("client %p: closing connection", client);
826 if (priv->connection) {
827 if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
828 g_mutex_lock (&tunnels_lock);
829 /* remove from tunnelids */
830 g_hash_table_remove (tunnels, tunnelid);
831 g_mutex_unlock (&tunnels_lock);
833 gst_rtsp_connection_close (priv->connection);
836 /* connection is now closed, destroy the watch which will also cause the
837 * closed signal to be emitted */
839 GST_DEBUG ("client %p: destroying watch", client);
840 g_source_destroy ((GSource *) priv->watch);
842 gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
847 default_make_path_from_uri (GstRTSPClient * client, const GstRTSPUrl * uri)
852 path = g_strconcat (uri->abspath, "?", uri->query, NULL);
854 path = g_strdup (uri->abspath);
860 handle_teardown_request (GstRTSPClient * client, GstRTSPContext * ctx)
862 GstRTSPClientPrivate *priv = client->priv;
863 GstRTSPClientClass *klass;
864 GstRTSPSession *session;
865 GstRTSPSessionMedia *sessmedia;
866 GstRTSPStatusCode code;
869 gboolean keep_session;
874 session = ctx->session;
879 klass = GST_RTSP_CLIENT_GET_CLASS (client);
880 path = klass->make_path_from_uri (client, ctx->uri);
882 /* get a handle to the configuration of the media in the session */
883 sessmedia = gst_rtsp_session_get_media (session, path, &matched);
887 /* only aggregate control for now.. */
888 if (path[matched] != '\0')
893 ctx->sessmedia = sessmedia;
895 /* we emit the signal before closing the connection */
896 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST],
899 /* make sure we unblock the backlog and don't accept new messages
901 if (priv->watch != NULL)
902 gst_rtsp_watch_set_flushing (priv->watch, TRUE);
904 /* unlink the all TCP callbacks */
905 unlink_session_transports (client, session, sessmedia);
907 gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
909 /* allow messages again so that we can send the reply */
910 if (priv->watch != NULL)
911 gst_rtsp_watch_set_flushing (priv->watch, FALSE);
913 /* unmanage the media in the session, returns false if all media session
915 keep_session = gst_rtsp_session_release_media (session, sessmedia);
917 /* construct the response now */
918 code = GST_RTSP_STS_OK;
919 gst_rtsp_message_init_response (ctx->response, code,
920 gst_rtsp_status_as_text (code), ctx->request);
922 send_message (client, ctx, ctx->response, TRUE);
925 /* remove the session */
926 gst_rtsp_session_pool_remove (priv->session_pool, session);
934 GST_ERROR ("client %p: no session", client);
935 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
940 GST_ERROR ("client %p: no uri supplied", client);
941 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
946 GST_ERROR ("client %p: no media for uri", client);
947 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
953 GST_ERROR ("client %p: no aggregate path %s", client, path);
954 send_generic_response (client,
955 GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
962 default_params_set (GstRTSPClient * client, GstRTSPContext * ctx)
966 res = gst_rtsp_params_set (client, ctx);
972 default_params_get (GstRTSPClient * client, GstRTSPContext * ctx)
976 res = gst_rtsp_params_get (client, ctx);
982 default_handle_get_param_request (GstRTSPClient * client, GstRTSPContext * ctx)
988 res = gst_rtsp_message_get_body (ctx->request, &data, &size);
989 if (res != GST_RTSP_OK)
993 /* no body, keep-alive request */
994 send_generic_response (client, GST_RTSP_STS_OK, ctx);
996 /* there is a body, handle the params */
997 res = GST_RTSP_CLIENT_GET_CLASS (client)->params_get (client, ctx);
998 if (res != GST_RTSP_OK)
1001 send_message (client, ctx, ctx->response, FALSE);
1004 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST],
1012 GST_ERROR ("client %p: bad request", client);
1013 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1019 default_handle_set_param_request (GstRTSPClient * client, GstRTSPContext * ctx)
1025 res = gst_rtsp_message_get_body (ctx->request, &data, &size);
1026 if (res != GST_RTSP_OK)
1030 /* no body, keep-alive request */
1031 send_generic_response (client, GST_RTSP_STS_OK, ctx);
1033 /* there is a body, handle the params */
1034 res = GST_RTSP_CLIENT_GET_CLASS (client)->params_set (client, ctx);
1035 if (res != GST_RTSP_OK)
1038 send_message (client, ctx, ctx->response, FALSE);
1041 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST],
1049 GST_ERROR ("client %p: bad request", client);
1050 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1056 handle_pause_request (GstRTSPClient * client, GstRTSPContext * ctx)
1058 GstRTSPSession *session;
1059 GstRTSPClientClass *klass;
1060 GstRTSPSessionMedia *sessmedia;
1061 GstRTSPStatusCode code;
1062 GstRTSPState rtspstate;
1066 if (!(session = ctx->session))
1072 klass = GST_RTSP_CLIENT_GET_CLASS (client);
1073 path = klass->make_path_from_uri (client, ctx->uri);
1075 /* get a handle to the configuration of the media in the session */
1076 sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1080 if (path[matched] != '\0')
1085 ctx->sessmedia = sessmedia;
1087 rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1088 /* the session state must be playing or recording */
1089 if (rtspstate != GST_RTSP_STATE_PLAYING &&
1090 rtspstate != GST_RTSP_STATE_RECORDING)
1093 /* unlink the all TCP callbacks */
1094 unlink_session_transports (client, session, sessmedia);
1096 /* then pause sending */
1097 gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PAUSED);
1099 /* construct the response now */
1100 code = GST_RTSP_STS_OK;
1101 gst_rtsp_message_init_response (ctx->response, code,
1102 gst_rtsp_status_as_text (code), ctx->request);
1104 send_message (client, ctx, ctx->response, FALSE);
1106 /* the state is now READY */
1107 gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
1109 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST], 0, ctx);
1116 GST_ERROR ("client %p: no seesion", client);
1117 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1122 GST_ERROR ("client %p: no uri supplied", client);
1123 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1128 GST_ERROR ("client %p: no media for uri", client);
1129 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1135 GST_ERROR ("client %p: no aggregate path %s", client, path);
1136 send_generic_response (client,
1137 GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
1143 GST_ERROR ("client %p: not PLAYING or RECORDING", client);
1144 send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
1150 /* convert @url and @path to a URL used as a content base for the factory
1151 * located at @path */
1153 make_base_url (GstRTSPClient * client, GstRTSPUrl * url, const gchar * path)
1159 /* check for trailing '/' and append one */
1160 trail = (path[strlen (path) - 1] != '/' ? "/" : "");
1165 tmp.abspath = g_strdup_printf ("%s%s", path, trail);
1167 result = gst_rtsp_url_get_request_uri (&tmp);
1168 g_free (tmp.abspath);
1174 handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
1176 GstRTSPSession *session;
1177 GstRTSPClientClass *klass;
1178 GstRTSPSessionMedia *sessmedia;
1179 GstRTSPMedia *media;
1180 GstRTSPStatusCode code;
1183 GstRTSPTimeRange *range;
1185 GstRTSPState rtspstate;
1186 GstRTSPRangeUnit unit = GST_RTSP_RANGE_NPT;
1187 gchar *path, *rtpinfo;
1190 if (!(session = ctx->session))
1193 if (!(uri = ctx->uri))
1196 klass = GST_RTSP_CLIENT_GET_CLASS (client);
1197 path = klass->make_path_from_uri (client, uri);
1199 /* get a handle to the configuration of the media in the session */
1200 sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1204 if (path[matched] != '\0')
1209 ctx->sessmedia = sessmedia;
1210 ctx->media = media = gst_rtsp_session_media_get_media (sessmedia);
1212 /* the session state must be playing or ready */
1213 rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1214 if (rtspstate != GST_RTSP_STATE_PLAYING && rtspstate != GST_RTSP_STATE_READY)
1217 /* in play we first unsuspend, media could be suspended from SDP or PAUSED */
1218 if (!gst_rtsp_media_unsuspend (media))
1219 goto unsuspend_failed;
1221 /* parse the range header if we have one */
1222 res = gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RANGE, &str, 0);
1223 if (res == GST_RTSP_OK) {
1224 if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
1225 /* we have a range, seek to the position */
1227 gst_rtsp_media_seek (media, range);
1228 gst_rtsp_range_free (range);
1232 /* link the all TCP callbacks */
1233 link_session_transports (client, session, sessmedia);
1235 /* grab RTPInfo from the media now */
1236 rtpinfo = gst_rtsp_session_media_get_rtpinfo (sessmedia);
1238 /* construct the response now */
1239 code = GST_RTSP_STS_OK;
1240 gst_rtsp_message_init_response (ctx->response, code,
1241 gst_rtsp_status_as_text (code), ctx->request);
1243 /* add the RTP-Info header */
1245 gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_RTP_INFO,
1249 str = gst_rtsp_media_get_range_string (media, TRUE, unit);
1251 gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_RANGE, str);
1253 send_message (client, ctx, ctx->response, FALSE);
1255 /* start playing after sending the response */
1256 gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PLAYING);
1258 gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_PLAYING);
1260 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST], 0, ctx);
1267 GST_ERROR ("client %p: no session", client);
1268 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1273 GST_ERROR ("client %p: no uri supplied", client);
1274 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1279 GST_ERROR ("client %p: media not found", client);
1280 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1285 GST_ERROR ("client %p: no aggregate path %s", client, path);
1286 send_generic_response (client,
1287 GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
1293 GST_ERROR ("client %p: not PLAYING or READY", client);
1294 send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
1300 GST_ERROR ("client %p: unsuspend failed", client);
1301 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1307 do_keepalive (GstRTSPSession * session)
1309 GST_INFO ("keep session %p alive", session);
1310 gst_rtsp_session_touch (session);
1313 /* parse @transport and return a valid transport in @tr. only transports
1314 * supported by @stream are returned. Returns FALSE if no valid transport
1317 parse_transport (const char *transport, GstRTSPStream * stream,
1318 GstRTSPTransport * tr)
1325 gst_rtsp_transport_init (tr);
1327 GST_DEBUG ("parsing transports %s", transport);
1329 transports = g_strsplit (transport, ",", 0);
1331 /* loop through the transports, try to parse */
1332 for (i = 0; transports[i]; i++) {
1333 res = gst_rtsp_transport_parse (transports[i], tr);
1334 if (res != GST_RTSP_OK) {
1335 /* no valid transport, search some more */
1336 GST_WARNING ("could not parse transport %s", transports[i]);
1340 /* we have a transport, see if it's supported */
1341 if (!gst_rtsp_stream_is_transport_supported (stream, tr)) {
1342 GST_WARNING ("unsupported transport %s", transports[i]);
1346 /* we have a valid transport */
1347 GST_INFO ("found valid transport %s", transports[i]);
1352 gst_rtsp_transport_init (tr);
1354 g_strfreev (transports);
1360 default_configure_client_media (GstRTSPClient * client, GstRTSPMedia * media,
1361 GstRTSPStream * stream, GstRTSPContext * ctx)
1363 GstRTSPMessage *request = ctx->request;
1364 gchar *blocksize_str;
1366 if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_BLOCKSIZE,
1367 &blocksize_str, 0) == GST_RTSP_OK) {
1371 blocksize = g_ascii_strtoull (blocksize_str, &end, 10);
1372 if (end == blocksize_str)
1375 /* we don't want to change the mtu when this media
1376 * can be shared because it impacts other clients */
1377 if (gst_rtsp_media_is_shared (media))
1380 if (blocksize > G_MAXUINT)
1381 blocksize = G_MAXUINT;
1383 gst_rtsp_stream_set_mtu (stream, blocksize);
1391 GST_ERROR_OBJECT (client, "failed to parse blocksize");
1392 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1398 default_configure_client_transport (GstRTSPClient * client,
1399 GstRTSPContext * ctx, GstRTSPTransport * ct)
1401 GstRTSPClientPrivate *priv = client->priv;
1403 /* we have a valid transport now, set the destination of the client. */
1404 if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
1405 gboolean use_client_settings;
1407 use_client_settings =
1408 gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS);
1410 if (ct->destination && use_client_settings) {
1411 GstRTSPAddress *addr;
1413 addr = gst_rtsp_stream_reserve_address (ctx->stream, ct->destination,
1414 ct->port.min, ct->port.max - ct->port.min + 1, ct->ttl);
1419 gst_rtsp_address_free (addr);
1421 GstRTSPAddress *addr;
1422 GSocketFamily family;
1424 family = priv->is_ipv6 ? G_SOCKET_FAMILY_IPV6 : G_SOCKET_FAMILY_IPV4;
1426 addr = gst_rtsp_stream_get_multicast_address (ctx->stream, family);
1430 g_free (ct->destination);
1431 ct->destination = g_strdup (addr->address);
1432 ct->port.min = addr->port;
1433 ct->port.max = addr->port + addr->n_ports - 1;
1434 ct->ttl = addr->ttl;
1436 gst_rtsp_address_free (addr);
1441 url = gst_rtsp_connection_get_url (priv->connection);
1442 g_free (ct->destination);
1443 ct->destination = g_strdup (url->host);
1445 if (ct->lower_transport & GST_RTSP_LOWER_TRANS_TCP) {
1447 GSocketAddress *addr;
1449 sock = gst_rtsp_connection_get_read_socket (priv->connection);
1450 if ((addr = g_socket_get_remote_address (sock, NULL))) {
1451 /* our read port is the sender port of client */
1452 ct->client_port.min =
1453 g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1454 g_object_unref (addr);
1456 if ((addr = g_socket_get_local_address (sock, NULL))) {
1457 ct->server_port.max =
1458 g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1459 g_object_unref (addr);
1461 sock = gst_rtsp_connection_get_write_socket (priv->connection);
1462 if ((addr = g_socket_get_remote_address (sock, NULL))) {
1463 /* our write port is the receiver port of client */
1464 ct->client_port.max =
1465 g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1466 g_object_unref (addr);
1468 if ((addr = g_socket_get_local_address (sock, NULL))) {
1469 ct->server_port.min =
1470 g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1471 g_object_unref (addr);
1473 /* check if the client selected channels for TCP */
1474 if (ct->interleaved.min == -1 || ct->interleaved.max == -1) {
1475 gst_rtsp_session_media_alloc_channels (ctx->sessmedia,
1485 GST_ERROR_OBJECT (client, "failed to acquire address for stream");
1490 static GstRTSPTransport *
1491 make_server_transport (GstRTSPClient * client, GstRTSPContext * ctx,
1492 GstRTSPTransport * ct)
1494 GstRTSPTransport *st;
1496 GSocketFamily family;
1498 /* prepare the server transport */
1499 gst_rtsp_transport_new (&st);
1501 st->trans = ct->trans;
1502 st->profile = ct->profile;
1503 st->lower_transport = ct->lower_transport;
1505 addr = g_inet_address_new_from_string (ct->destination);
1508 GST_ERROR ("failed to get inet addr from client destination");
1509 family = G_SOCKET_FAMILY_IPV4;
1511 family = g_inet_address_get_family (addr);
1512 g_object_unref (addr);
1516 switch (st->lower_transport) {
1517 case GST_RTSP_LOWER_TRANS_UDP:
1518 st->client_port = ct->client_port;
1519 gst_rtsp_stream_get_server_port (ctx->stream, &st->server_port, family);
1521 case GST_RTSP_LOWER_TRANS_UDP_MCAST:
1522 st->port = ct->port;
1523 st->destination = g_strdup (ct->destination);
1526 case GST_RTSP_LOWER_TRANS_TCP:
1527 st->interleaved = ct->interleaved;
1528 st->client_port = ct->client_port;
1529 st->server_port = ct->server_port;
1534 gst_rtsp_stream_get_ssrc (ctx->stream, &st->ssrc);
1539 #define AES_128_KEY_LEN 16
1540 #define AES_256_KEY_LEN 32
1542 #define HMAC_32_KEY_LEN 4
1543 #define HMAC_80_KEY_LEN 10
1546 mikey_apply_policy (GstCaps * caps, GstMIKEYMessage * msg, guint8 policy)
1548 const gchar *srtp_cipher;
1549 const gchar *srtp_auth;
1550 const GstMIKEYPayload *sp;
1553 /* loop over Security policy until we find one containing policy */
1555 if ((sp = gst_mikey_message_find_payload (msg, GST_MIKEY_PT_SP, i)) == NULL)
1558 if (((GstMIKEYPayloadSP *) sp)->policy == policy)
1562 /* the default ciphers */
1563 srtp_cipher = "aes-128-icm";
1564 srtp_auth = "hmac-sha1-80";
1566 /* now override the defaults with what is in the Security Policy */
1570 /* collect all the params and go over them */
1571 len = gst_mikey_payload_sp_get_n_params (sp);
1572 for (i = 0; i < len; i++) {
1573 const GstMIKEYPayloadSPParam *param =
1574 gst_mikey_payload_sp_get_param (sp, i);
1576 switch (param->type) {
1577 case GST_MIKEY_SP_SRTP_ENC_ALG:
1578 switch (param->val[0]) {
1580 srtp_cipher = "null";
1584 srtp_cipher = "aes-128-icm";
1590 case GST_MIKEY_SP_SRTP_ENC_KEY_LEN:
1591 switch (param->val[0]) {
1592 case AES_128_KEY_LEN:
1593 srtp_cipher = "aes-128-icm";
1595 case AES_256_KEY_LEN:
1596 srtp_cipher = "aes-256-icm";
1602 case GST_MIKEY_SP_SRTP_AUTH_ALG:
1603 switch (param->val[0]) {
1609 srtp_auth = "hmac-sha1-80";
1615 case GST_MIKEY_SP_SRTP_AUTH_KEY_LEN:
1616 switch (param->val[0]) {
1617 case HMAC_32_KEY_LEN:
1618 srtp_auth = "hmac-sha1-32";
1620 case HMAC_80_KEY_LEN:
1621 srtp_auth = "hmac-sha1-80";
1627 case GST_MIKEY_SP_SRTP_SRTP_ENC:
1629 case GST_MIKEY_SP_SRTP_SRTCP_ENC:
1636 /* now configure the SRTP parameters */
1637 gst_caps_set_simple (caps,
1638 "srtp-cipher", G_TYPE_STRING, srtp_cipher,
1639 "srtp-auth", G_TYPE_STRING, srtp_auth,
1640 "srtcp-cipher", G_TYPE_STRING, srtp_cipher,
1641 "srtcp-auth", G_TYPE_STRING, srtp_auth, NULL);
1647 handle_mikey_data (GstRTSPClient * client, GstRTSPContext * ctx,
1648 guint8 * data, gsize size)
1650 GstMIKEYMessage *msg;
1652 GstCaps *caps = NULL;
1653 GstMIKEYPayloadKEMAC *kemac;
1654 const GstMIKEYPayloadKeyData *pkd;
1657 /* the MIKEY message contains a CSB or crypto session bundle. It is a
1658 * set of Crypto Sessions protected with the same master key.
1659 * In the context of SRTP, an RTP and its RTCP stream is part of a
1661 if ((msg = gst_mikey_message_new_from_data (data, size, NULL, NULL)) == NULL)
1664 /* we can only handle SRTP crypto sessions for now */
1665 if (msg->map_type != GST_MIKEY_MAP_TYPE_SRTP)
1666 goto invalid_map_type;
1668 /* get the number of crypto sessions. This maps SSRC to its
1669 * security parameters */
1670 n_cs = gst_mikey_message_get_n_cs (msg);
1672 goto no_crypto_sessions;
1674 /* we also need keys */
1675 if (!(kemac = (GstMIKEYPayloadKEMAC *) gst_mikey_message_find_payload
1676 (msg, GST_MIKEY_PT_KEMAC, 0)))
1679 /* we don't support encrypted keys */
1680 if (kemac->enc_alg != GST_MIKEY_ENC_NULL
1681 || kemac->mac_alg != GST_MIKEY_MAC_NULL)
1682 goto unsupported_encryption;
1684 /* get Key data sub-payload */
1685 pkd = (const GstMIKEYPayloadKeyData *)
1686 gst_mikey_payload_kemac_get_sub (&kemac->pt, 0);
1689 gst_buffer_new_wrapped (g_memdup (pkd->key_data, pkd->key_len),
1692 /* go over all crypto sessions and create the security policy for each
1694 for (i = 0; i < n_cs; i++) {
1695 const GstMIKEYMapSRTP *map = gst_mikey_message_get_cs_srtp (msg, i);
1697 caps = gst_caps_new_simple ("application/x-srtp",
1698 "ssrc", G_TYPE_UINT, map->ssrc,
1699 "roc", G_TYPE_UINT, map->roc, "srtp-key", GST_TYPE_BUFFER, key, NULL);
1700 mikey_apply_policy (caps, msg, map->policy);
1702 gst_rtsp_stream_update_crypto (ctx->stream, map->ssrc, caps);
1703 gst_caps_unref (caps);
1705 gst_mikey_message_unref (msg);
1712 GST_DEBUG_OBJECT (client, "failed to parse MIKEY message");
1717 GST_DEBUG_OBJECT (client, "invalid map type %d", msg->map_type);
1718 goto cleanup_message;
1722 GST_DEBUG_OBJECT (client, "no crypto sessions");
1723 goto cleanup_message;
1727 GST_DEBUG_OBJECT (client, "no keys found");
1728 goto cleanup_message;
1730 unsupported_encryption:
1732 GST_DEBUG_OBJECT (client, "unsupported key encryption");
1733 goto cleanup_message;
1737 gst_mikey_message_unref (msg);
1742 #define IS_STRIP_CHAR(c) (g_ascii_isspace ((guchar)(c)) || ((c) == '\"'))
1745 strip_chars (gchar * str)
1752 if (!IS_STRIP_CHAR (str[len]))
1756 for (s = str; *s && IS_STRIP_CHAR (*s); s++);
1757 memmove (str, s, len + 1);
1760 /* KeyMgmt = "KeyMgmt" ":" key-mgmt-spec 0*("," key-mgmt-spec)
1761 * key-mgmt-spec = "prot" "=" KMPID ";" ["uri" "=" %x22 URI %x22 ";"]
1764 handle_keymgmt (GstRTSPClient * client, GstRTSPContext * ctx, gchar * keymgmt)
1769 specs = g_strsplit (keymgmt, ",", 0);
1770 for (i = 0; specs[i]; i++) {
1773 split = g_strsplit (specs[i], ";", 0);
1774 for (j = 0; split[j]; j++) {
1775 g_strstrip (split[j]);
1776 if (g_str_has_prefix (split[j], "prot=")) {
1777 g_strstrip (split[j] + 5);
1778 if (!g_str_equal (split[j] + 5, "mikey"))
1780 GST_DEBUG ("found mikey");
1781 } else if (g_str_has_prefix (split[j], "uri=")) {
1782 strip_chars (split[j] + 4);
1783 GST_DEBUG ("found uri '%s'", split[j] + 4);
1784 } else if (g_str_has_prefix (split[j], "data=")) {
1787 strip_chars (split[j] + 5);
1788 GST_DEBUG ("found data '%s'", split[j] + 5);
1789 data = g_base64_decode_inplace (split[j] + 5, &size);
1790 handle_mikey_data (client, ctx, data, size);
1798 handle_setup_request (GstRTSPClient * client, GstRTSPContext * ctx)
1800 GstRTSPClientPrivate *priv = client->priv;
1803 gchar *transport, *keymgmt;
1804 GstRTSPTransport *ct, *st;
1805 GstRTSPStatusCode code;
1806 GstRTSPSession *session;
1807 GstRTSPStreamTransport *trans;
1809 GstRTSPSessionMedia *sessmedia;
1810 GstRTSPMedia *media;
1811 GstRTSPStream *stream;
1812 GstRTSPState rtspstate;
1813 GstRTSPClientClass *klass;
1814 gchar *path, *control;
1816 gboolean new_session = FALSE;
1822 klass = GST_RTSP_CLIENT_GET_CLASS (client);
1823 path = klass->make_path_from_uri (client, uri);
1825 /* parse the transport */
1827 gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_TRANSPORT,
1829 if (res != GST_RTSP_OK)
1832 /* we create the session after parsing stuff so that we don't make
1833 * a session for malformed requests */
1834 if (priv->session_pool == NULL)
1837 session = ctx->session;
1840 g_object_ref (session);
1841 /* get a handle to the configuration of the media in the session, this can
1842 * return NULL if this is a new url to manage in this session. */
1843 sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1845 /* we need a new media configuration in this session */
1849 /* we have no session media, find one and manage it */
1850 if (sessmedia == NULL) {
1851 /* get a handle to the configuration of the media in the session */
1852 media = find_media (client, ctx, path, &matched);
1854 if ((media = gst_rtsp_session_media_get_media (sessmedia)))
1855 g_object_ref (media);
1857 goto media_not_found;
1859 /* no media, not found then */
1861 goto media_not_found_no_reply;
1863 /* FIXME-WFD : wfd url problem */
1865 if (path[matched] == '\0')
1866 goto control_not_found;
1868 /* path is what matched. */
1869 path[matched] = '\0';
1870 /* control is remainder */
1871 control = &path[matched + 1];
1873 control = g_strdup ("stream=0");
1876 /* find the stream now using the control part */
1877 stream = gst_rtsp_media_find_stream (media, control);
1879 goto stream_not_found;
1881 /* now we have a uri identifying a valid media and stream */
1882 ctx->stream = stream;
1885 if (session == NULL) {
1886 /* create a session if this fails we probably reached our session limit or
1888 if (!(session = gst_rtsp_session_pool_create (priv->session_pool)))
1889 goto service_unavailable;
1891 /* make sure this client is closed when the session is closed */
1892 client_watch_session (client, session);
1895 /* signal new session */
1896 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_NEW_SESSION], 0,
1899 ctx->session = session;
1902 if (!klass->configure_client_media (client, media, stream, ctx))
1903 goto configure_media_failed_no_reply;
1905 gst_rtsp_transport_new (&ct);
1907 /* parse and find a usable supported transport */
1908 if (!parse_transport (transport, stream, ct))
1909 goto unsupported_transports;
1911 /* update the client transport */
1912 if (!klass->configure_client_transport (client, ctx, ct))
1913 goto unsupported_client_transport;
1915 /* parse the keymgmt */
1916 if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_KEYMGMT,
1917 &keymgmt, 0) == GST_RTSP_OK) {
1918 if (!handle_keymgmt (client, ctx, keymgmt))
1922 if (sessmedia == NULL) {
1923 /* manage the media in our session now, if not done already */
1924 sessmedia = gst_rtsp_session_manage_media (session, path, media);
1925 /* if we stil have no media, error */
1926 if (sessmedia == NULL)
1927 goto sessmedia_unavailable;
1929 g_object_unref (media);
1932 ctx->sessmedia = sessmedia;
1934 /* set in the session media transport */
1935 trans = gst_rtsp_session_media_set_transport (sessmedia, stream, ct);
1937 /* configure the url used to set this transport, this we will use when
1938 * generating the response for the PLAY request */
1939 gst_rtsp_stream_transport_set_url (trans, uri);
1941 /* configure keepalive for this transport */
1942 gst_rtsp_stream_transport_set_keepalive (trans,
1943 (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
1945 /* create and serialize the server transport */
1946 st = make_server_transport (client, ctx, ct);
1947 trans_str = gst_rtsp_transport_as_text (st);
1949 /* FIXME-WFD : Temporarily force to set profile string */
1950 trans_str = g_strjoinv ("RTP/AVP/UDP", g_strsplit (trans_str, "RTP/AVP", -1));
1952 gst_rtsp_transport_free (st);
1954 /* construct the response now */
1955 code = GST_RTSP_STS_OK;
1956 gst_rtsp_message_init_response (ctx->response, code,
1957 gst_rtsp_status_as_text (code), ctx->request);
1959 gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_TRANSPORT,
1963 send_message (client, ctx, ctx->response, FALSE);
1965 /* update the state */
1966 rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1967 switch (rtspstate) {
1968 case GST_RTSP_STATE_PLAYING:
1969 case GST_RTSP_STATE_RECORDING:
1970 case GST_RTSP_STATE_READY:
1971 /* no state change */
1974 gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
1977 g_object_unref (session);
1980 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST], 0, ctx);
1987 GST_ERROR ("client %p: no uri", client);
1988 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1993 GST_ERROR ("client %p: no transport", client);
1994 send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
1999 GST_ERROR ("client %p: no session pool configured", client);
2000 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
2003 media_not_found_no_reply:
2005 GST_ERROR ("client %p: media '%s' not found", client, path);
2006 /* error reply is already sent */
2011 GST_ERROR ("client %p: media '%s' not found", client, path);
2012 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2017 GST_ERROR ("client %p: no control in path '%s'", client, path);
2018 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2019 g_object_unref (media);
2024 GST_ERROR ("client %p: stream '%s' not found", client, control);
2025 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2026 g_object_unref (media);
2029 service_unavailable:
2031 GST_ERROR ("client %p: can't create session", client);
2032 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
2033 g_object_unref (media);
2036 sessmedia_unavailable:
2038 GST_ERROR ("client %p: can't create session media", client);
2039 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
2040 g_object_unref (media);
2041 goto cleanup_session;
2043 configure_media_failed_no_reply:
2045 GST_ERROR ("client %p: configure_media failed", client);
2046 /* error reply is already sent */
2047 goto cleanup_session;
2049 unsupported_transports:
2051 GST_ERROR ("client %p: unsupported transports", client);
2052 send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
2053 goto cleanup_transport;
2055 unsupported_client_transport:
2057 GST_ERROR ("client %p: unsupported client transport", client);
2058 send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
2059 goto cleanup_transport;
2063 GST_ERROR ("client %p: keymgmt error", client);
2064 send_generic_response (client, GST_RTSP_STS_KEY_MANAGEMENT_FAILURE, ctx);
2065 goto cleanup_transport;
2069 gst_rtsp_transport_free (ct);
2072 gst_rtsp_session_pool_remove (priv->session_pool, session);
2073 g_object_unref (session);
2080 static GstSDPMessage *
2081 create_sdp (GstRTSPClient * client, GstRTSPMedia * media)
2083 GstRTSPClientPrivate *priv = client->priv;
2088 gst_sdp_message_new (&sdp);
2090 /* some standard things first */
2091 gst_sdp_message_set_version (sdp, "0");
2098 gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", proto,
2101 gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
2102 gst_sdp_message_set_information (sdp, "rtsp-server");
2103 gst_sdp_message_add_time (sdp, "0", "0", NULL);
2104 gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
2105 gst_sdp_message_add_attribute (sdp, "type", "broadcast");
2106 gst_sdp_message_add_attribute (sdp, "control", "*");
2108 info.is_ipv6 = priv->is_ipv6;
2109 info.server_ip = priv->server_ip;
2111 /* create an SDP for the media object */
2112 if (!gst_rtsp_media_setup_sdp (media, sdp, &info))
2120 GST_ERROR ("client %p: could not create SDP", client);
2121 gst_sdp_message_free (sdp);
2126 /* for the describe we must generate an SDP */
2128 handle_describe_request (GstRTSPClient * client, GstRTSPContext * ctx)
2130 GstRTSPClientPrivate *priv = client->priv;
2135 GstRTSPMedia *media;
2136 GstRTSPClientClass *klass;
2138 klass = GST_RTSP_CLIENT_GET_CLASS (client);
2143 /* check what kind of format is accepted, we don't really do anything with it
2144 * and always return SDP for now. */
2149 gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_ACCEPT,
2151 if (res == GST_RTSP_ENOTIMPL)
2154 if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
2158 if (!priv->mount_points)
2159 goto no_mount_points;
2161 if (!(path = gst_rtsp_mount_points_make_path (priv->mount_points, ctx->uri)))
2164 /* find the media object for the uri */
2165 if (!(media = find_media (client, ctx, path, NULL)))
2168 /* create an SDP for the media object on this client */
2169 if (!(sdp = klass->create_sdp (client, media)))
2172 /* we suspend after the describe */
2173 gst_rtsp_media_suspend (media);
2174 g_object_unref (media);
2176 gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
2177 gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
2179 gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_CONTENT_TYPE,
2182 /* content base for some clients that might screw up creating the setup uri */
2183 str = make_base_url (client, ctx->uri, path);
2186 GST_INFO ("adding content-base: %s", str);
2187 gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_CONTENT_BASE, str);
2189 /* add SDP to the response body */
2190 str = gst_sdp_message_as_text (sdp);
2191 gst_rtsp_message_take_body (ctx->response, (guint8 *) str, strlen (str));
2192 gst_sdp_message_free (sdp);
2194 send_message (client, ctx, ctx->response, FALSE);
2196 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST],
2204 GST_ERROR ("client %p: no uri", client);
2205 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2210 GST_ERROR ("client %p: no mount points configured", client);
2211 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2216 GST_ERROR ("client %p: can't find path for url", client);
2217 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2222 GST_ERROR ("client %p: no media", client);
2224 /* error reply is already sent */
2229 GST_ERROR ("client %p: can't create SDP", client);
2230 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
2232 g_object_unref (media);
2238 default_handle_options_request (GstRTSPClient * client, GstRTSPContext * ctx)
2240 GstRTSPMethod options;
2243 options = GST_RTSP_DESCRIBE |
2248 GST_RTSP_GET_PARAMETER | GST_RTSP_SET_PARAMETER | GST_RTSP_TEARDOWN;
2250 str = gst_rtsp_options_as_text (options);
2252 gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
2253 gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
2255 gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_PUBLIC, str);
2258 send_message (client, ctx, ctx->response, FALSE);
2260 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST],
2266 /* remove duplicate and trailing '/' */
2268 sanitize_uri (GstRTSPUrl * uri)
2272 gboolean have_slash, prev_slash;
2274 s = d = uri->abspath;
2275 len = strlen (uri->abspath);
2279 for (i = 0; i < len; i++) {
2280 have_slash = s[i] == '/';
2282 if (!have_slash || !prev_slash)
2284 prev_slash = have_slash;
2286 len = d - uri->abspath;
2287 /* don't remove the first slash if that's the only thing left */
2288 if (len > 1 && *(d - 1) == '/')
2293 /* is called when the session is removed from its session pool. */
2295 client_session_removed (GstRTSPSessionPool * pool, GstRTSPSession * session,
2296 GstRTSPClient * client)
2298 GstRTSPClientPrivate *priv = client->priv;
2300 GST_INFO ("client %p: session %p removed", client, session);
2302 g_mutex_lock (&priv->lock);
2303 client_unwatch_session (client, session, NULL);
2304 g_mutex_unlock (&priv->lock);
2307 /* Returns TRUE if there are no Require headers, otherwise returns FALSE
2308 * and also returns a newly-allocated string of (comma-separated) unsupported
2309 * options in the unsupported_reqs variable .
2311 * There may be multiple Require headers, but we must send one single
2312 * Unsupported header with all the unsupported options as response. If
2313 * an incoming Require header contained a comma-separated list of options
2314 * GstRtspConnection will already have split that list up into multiple
2317 * TODO: allow the application to decide what features are supported
2320 check_request_requirements (GstRTSPMessage * msg, gchar ** unsupported_reqs)
2323 GPtrArray *arr = NULL;
2329 res = gst_rtsp_message_get_header (msg, GST_RTSP_HDR_REQUIRE, &reqs, i++);
2331 if (res == GST_RTSP_ENOTIMPL)
2335 arr = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
2337 g_ptr_array_add (arr, g_strdup (reqs));
2341 /* if we don't have any Require headers at all, all is fine */
2345 /* otherwise we've now processed at all the Require headers */
2346 g_ptr_array_add (arr, NULL);
2348 /* for now we don't commit to supporting anything, so will just report
2349 * all of the required options as unsupported */
2350 *unsupported_reqs = g_strjoinv (", ", (gchar **) arr->pdata);
2352 g_ptr_array_unref (arr);
2357 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
2359 GstRTSPClientPrivate *priv = client->priv;
2360 GstRTSPMethod method;
2361 const gchar *uristr;
2362 GstRTSPUrl *uri = NULL;
2363 GstRTSPVersion version;
2365 GstRTSPSession *session = NULL;
2366 GstRTSPContext sctx = { NULL }, *ctx;
2367 GstRTSPMessage response = { 0 };
2368 gchar *unsupported_reqs = NULL;
2370 GstRTSPClientClass *klass;
2372 klass = GST_RTSP_CLIENT_GET_CLASS (client);
2374 if (!(ctx = gst_rtsp_context_get_current ())) {
2376 ctx->auth = priv->auth;
2377 gst_rtsp_context_push_current (ctx);
2380 ctx->conn = priv->connection;
2381 ctx->client = client;
2382 ctx->request = request;
2383 ctx->response = &response;
2385 if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
2386 gst_rtsp_message_dump (request);
2389 gst_rtsp_message_parse_request (request, &method, &uristr, &version);
2391 GST_INFO ("client %p: received a request %s %s %s", client,
2392 gst_rtsp_method_as_text (method), uristr,
2393 gst_rtsp_version_as_text (version));
2395 /* we can only handle 1.0 requests */
2396 if (version != GST_RTSP_VERSION_1_0)
2399 ctx->method = method;
2401 /* we always try to parse the url first */
2402 if (strcmp (uristr, "*") == 0) {
2403 /* special case where we have * as uri, keep uri = NULL */
2404 } else if (gst_rtsp_url_parse (uristr, &uri) != GST_RTSP_OK) {
2405 /* check if the uristr is an absolute path <=> scheme and host information
2409 scheme = g_uri_parse_scheme (uristr);
2410 if (scheme == NULL && g_str_has_prefix (uristr, "/")) {
2411 gchar *absolute_uristr = NULL;
2413 GST_WARNING_OBJECT (client, "request doesn't contain absolute url");
2414 if (priv->server_ip == NULL) {
2415 GST_WARNING_OBJECT (client, "host information missing");
2420 g_strdup_printf ("rtsp://%s%s", priv->server_ip, uristr);
2422 GST_DEBUG_OBJECT (client, "absolute url: %s", absolute_uristr);
2423 if (gst_rtsp_url_parse (absolute_uristr, &uri) != GST_RTSP_OK) {
2424 g_free (absolute_uristr);
2427 g_free (absolute_uristr);
2434 /* get the session if there is any */
2435 res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
2436 if (res == GST_RTSP_OK) {
2437 if (priv->session_pool == NULL)
2440 /* we had a session in the request, find it again */
2441 if (!(session = gst_rtsp_session_pool_find (priv->session_pool, sessid)))
2442 goto session_not_found;
2444 /* we add the session to the client list of watched sessions. When a session
2445 * disappears because it times out, we will be notified. If all sessions are
2446 * gone, we will close the connection */
2447 client_watch_session (client, session);
2450 /* sanitize the uri */
2454 ctx->session = session;
2456 if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_URL))
2457 goto not_authorized;
2460 /* FIXME-WFD : How does it handle this */
2461 /* handle any 'Require' headers */
2462 if (!check_request_requirements (ctx->request, &unsupported_reqs))
2463 goto unsupported_requirement;
2466 /* now see what is asked and dispatch to a dedicated handler */
2468 case GST_RTSP_OPTIONS:
2469 klass->handle_options_request (client, ctx);
2471 case GST_RTSP_DESCRIBE:
2472 handle_describe_request (client, ctx);
2474 case GST_RTSP_SETUP:
2475 handle_setup_request (client, ctx);
2478 handle_play_request (client, ctx);
2480 case GST_RTSP_PAUSE:
2481 handle_pause_request (client, ctx);
2483 case GST_RTSP_TEARDOWN:
2484 handle_teardown_request (client, ctx);
2486 case GST_RTSP_SET_PARAMETER:
2487 klass->handle_set_param_request (client, ctx);
2489 case GST_RTSP_GET_PARAMETER:
2490 klass->handle_get_param_request (client, ctx);
2492 case GST_RTSP_ANNOUNCE:
2493 case GST_RTSP_RECORD:
2494 case GST_RTSP_REDIRECT:
2495 goto not_implemented;
2496 case GST_RTSP_INVALID:
2503 gst_rtsp_context_pop_current (ctx);
2505 g_object_unref (session);
2507 gst_rtsp_url_free (uri);
2513 GST_ERROR ("client %p: version %d not supported", client, version);
2514 send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
2520 GST_ERROR ("client %p: bad request", client);
2521 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2526 GST_ERROR ("client %p: no pool configured", client);
2527 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
2532 GST_ERROR ("client %p: session not found", client);
2533 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
2538 GST_ERROR ("client %p: not allowed", client);
2539 /* error reply is already sent */
2542 unsupported_requirement:
2544 GST_ERROR ("client %p: Required option is not supported (%s)", client,
2546 send_option_not_supported_response (client, ctx, unsupported_reqs);
2547 g_free (unsupported_reqs);
2552 GST_ERROR ("client %p: method %d not implemented", client, method);
2553 send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, ctx);
2560 handle_response (GstRTSPClient * client, GstRTSPMessage * response)
2562 GstRTSPClientPrivate *priv = client->priv;
2564 GstRTSPSession *session = NULL;
2565 GstRTSPContext sctx = { NULL }, *ctx;
2568 if (!(ctx = gst_rtsp_context_get_current ())) {
2570 ctx->auth = priv->auth;
2571 gst_rtsp_context_push_current (ctx);
2574 ctx->conn = priv->connection;
2575 ctx->client = client;
2576 ctx->request = NULL;
2578 ctx->method = GST_RTSP_INVALID;
2579 ctx->response = response;
2581 if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
2582 gst_rtsp_message_dump (response);
2585 GST_INFO ("client %p: received a response", client);
2587 /* get the session if there is any */
2589 gst_rtsp_message_get_header (response, GST_RTSP_HDR_SESSION, &sessid, 0);
2590 if (res == GST_RTSP_OK) {
2591 if (priv->session_pool == NULL)
2594 /* we had a session in the request, find it again */
2595 if (!(session = gst_rtsp_session_pool_find (priv->session_pool, sessid)))
2596 goto session_not_found;
2598 /* we add the session to the client list of watched sessions. When a session
2599 * disappears because it times out, we will be notified. If all sessions are
2600 * gone, we will close the connection */
2601 client_watch_session (client, session);
2604 ctx->session = session;
2606 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_HANDLE_RESPONSE],
2611 gst_rtsp_context_pop_current (ctx);
2613 g_object_unref (session);
2618 GST_ERROR ("client %p: no pool configured", client);
2623 GST_ERROR ("client %p: session not found", client);
2629 handle_data (GstRTSPClient * client, GstRTSPMessage * message)
2631 GstRTSPClientPrivate *priv = client->priv;
2640 /* find the stream for this message */
2641 res = gst_rtsp_message_parse_data (message, &channel);
2642 if (res != GST_RTSP_OK)
2645 gst_rtsp_message_steal_body (message, &data, &size);
2647 buffer = gst_buffer_new_wrapped (data, size);
2650 for (walk = priv->transports; walk; walk = g_list_next (walk)) {
2651 GstRTSPStreamTransport *trans;
2652 GstRTSPStream *stream;
2653 const GstRTSPTransport *tr;
2657 tr = gst_rtsp_stream_transport_get_transport (trans);
2658 stream = gst_rtsp_stream_transport_get_stream (trans);
2660 /* check for TCP transport */
2661 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
2662 /* dispatch to the stream based on the channel number */
2663 if (tr->interleaved.min == channel) {
2664 gst_rtsp_stream_recv_rtp (stream, buffer);
2667 } else if (tr->interleaved.max == channel) {
2668 gst_rtsp_stream_recv_rtcp (stream, buffer);
2675 gst_buffer_unref (buffer);
2679 * gst_rtsp_client_set_session_pool:
2680 * @client: a #GstRTSPClient
2681 * @pool: (transfer none): a #GstRTSPSessionPool
2683 * Set @pool as the sessionpool for @client which it will use to find
2684 * or allocate sessions. the sessionpool is usually inherited from the server
2685 * that created the client but can be overridden later.
2688 gst_rtsp_client_set_session_pool (GstRTSPClient * client,
2689 GstRTSPSessionPool * pool)
2691 GstRTSPSessionPool *old;
2692 GstRTSPClientPrivate *priv;
2694 g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2696 priv = client->priv;
2699 g_object_ref (pool);
2701 g_mutex_lock (&priv->lock);
2702 old = priv->session_pool;
2703 priv->session_pool = pool;
2705 if (priv->session_removed_id) {
2706 g_signal_handler_disconnect (old, priv->session_removed_id);
2707 priv->session_removed_id = 0;
2709 g_mutex_unlock (&priv->lock);
2711 /* FIXME, should remove all sessions from the old pool for this client */
2713 g_object_unref (old);
2717 * gst_rtsp_client_get_session_pool:
2718 * @client: a #GstRTSPClient
2720 * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
2722 * Returns: (transfer full): a #GstRTSPSessionPool, unref after usage.
2724 GstRTSPSessionPool *
2725 gst_rtsp_client_get_session_pool (GstRTSPClient * client)
2727 GstRTSPClientPrivate *priv;
2728 GstRTSPSessionPool *result;
2730 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2732 priv = client->priv;
2734 g_mutex_lock (&priv->lock);
2735 if ((result = priv->session_pool))
2736 g_object_ref (result);
2737 g_mutex_unlock (&priv->lock);
2743 * gst_rtsp_client_set_mount_points:
2744 * @client: a #GstRTSPClient
2745 * @mounts: (transfer none): a #GstRTSPMountPoints
2747 * Set @mounts as the mount points for @client which it will use to map urls
2748 * to media streams. These mount points are usually inherited from the server that
2749 * created the client but can be overriden later.
2752 gst_rtsp_client_set_mount_points (GstRTSPClient * client,
2753 GstRTSPMountPoints * mounts)
2755 GstRTSPClientPrivate *priv;
2756 GstRTSPMountPoints *old;
2758 g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2760 priv = client->priv;
2763 g_object_ref (mounts);
2765 g_mutex_lock (&priv->lock);
2766 old = priv->mount_points;
2767 priv->mount_points = mounts;
2768 g_mutex_unlock (&priv->lock);
2771 g_object_unref (old);
2775 * gst_rtsp_client_get_mount_points:
2776 * @client: a #GstRTSPClient
2778 * Get the #GstRTSPMountPoints object that @client uses to manage its sessions.
2780 * Returns: (transfer full): a #GstRTSPMountPoints, unref after usage.
2782 GstRTSPMountPoints *
2783 gst_rtsp_client_get_mount_points (GstRTSPClient * client)
2785 GstRTSPClientPrivate *priv;
2786 GstRTSPMountPoints *result;
2788 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2790 priv = client->priv;
2792 g_mutex_lock (&priv->lock);
2793 if ((result = priv->mount_points))
2794 g_object_ref (result);
2795 g_mutex_unlock (&priv->lock);
2801 * gst_rtsp_client_set_auth:
2802 * @client: a #GstRTSPClient
2803 * @auth: (transfer none): a #GstRTSPAuth
2805 * configure @auth to be used as the authentication manager of @client.
2808 gst_rtsp_client_set_auth (GstRTSPClient * client, GstRTSPAuth * auth)
2810 GstRTSPClientPrivate *priv;
2813 g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2815 priv = client->priv;
2818 g_object_ref (auth);
2820 g_mutex_lock (&priv->lock);
2823 g_mutex_unlock (&priv->lock);
2826 g_object_unref (old);
2831 * gst_rtsp_client_get_auth:
2832 * @client: a #GstRTSPClient
2834 * Get the #GstRTSPAuth used as the authentication manager of @client.
2836 * Returns: (transfer full): the #GstRTSPAuth of @client. g_object_unref() after
2840 gst_rtsp_client_get_auth (GstRTSPClient * client)
2842 GstRTSPClientPrivate *priv;
2843 GstRTSPAuth *result;
2845 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2847 priv = client->priv;
2849 g_mutex_lock (&priv->lock);
2850 if ((result = priv->auth))
2851 g_object_ref (result);
2852 g_mutex_unlock (&priv->lock);
2858 * gst_rtsp_client_set_thread_pool:
2859 * @client: a #GstRTSPClient
2860 * @pool: (transfer none): a #GstRTSPThreadPool
2862 * configure @pool to be used as the thread pool of @client.
2865 gst_rtsp_client_set_thread_pool (GstRTSPClient * client,
2866 GstRTSPThreadPool * pool)
2868 GstRTSPClientPrivate *priv;
2869 GstRTSPThreadPool *old;
2871 g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2873 priv = client->priv;
2876 g_object_ref (pool);
2878 g_mutex_lock (&priv->lock);
2879 old = priv->thread_pool;
2880 priv->thread_pool = pool;
2881 g_mutex_unlock (&priv->lock);
2884 g_object_unref (old);
2888 * gst_rtsp_client_get_thread_pool:
2889 * @client: a #GstRTSPClient
2891 * Get the #GstRTSPThreadPool used as the thread pool of @client.
2893 * Returns: (transfer full): the #GstRTSPThreadPool of @client. g_object_unref() after
2897 gst_rtsp_client_get_thread_pool (GstRTSPClient * client)
2899 GstRTSPClientPrivate *priv;
2900 GstRTSPThreadPool *result;
2902 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2904 priv = client->priv;
2906 g_mutex_lock (&priv->lock);
2907 if ((result = priv->thread_pool))
2908 g_object_ref (result);
2909 g_mutex_unlock (&priv->lock);
2915 * gst_rtsp_client_set_connection:
2916 * @client: a #GstRTSPClient
2917 * @conn: (transfer full): a #GstRTSPConnection
2919 * Set the #GstRTSPConnection of @client. This function takes ownership of
2922 * Returns: %TRUE on success.
2925 gst_rtsp_client_set_connection (GstRTSPClient * client,
2926 GstRTSPConnection * conn)
2928 GstRTSPClientPrivate *priv;
2929 GSocket *read_socket;
2930 GSocketAddress *address;
2932 GError *error = NULL;
2934 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
2935 g_return_val_if_fail (conn != NULL, FALSE);
2937 priv = client->priv;
2939 read_socket = gst_rtsp_connection_get_read_socket (conn);
2941 if (!(address = g_socket_get_local_address (read_socket, &error)))
2944 g_free (priv->server_ip);
2945 /* keep the original ip that the client connected to */
2946 if (G_IS_INET_SOCKET_ADDRESS (address)) {
2947 GInetAddress *iaddr;
2949 iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address));
2951 /* socket might be ipv6 but adress still ipv4 */
2952 priv->is_ipv6 = g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV6;
2953 priv->server_ip = g_inet_address_to_string (iaddr);
2954 g_object_unref (address);
2956 priv->is_ipv6 = g_socket_get_family (read_socket) == G_SOCKET_FAMILY_IPV6;
2957 priv->server_ip = g_strdup ("unknown");
2960 GST_INFO ("client %p connected to server ip %s, ipv6 = %d", client,
2961 priv->server_ip, priv->is_ipv6);
2963 url = gst_rtsp_connection_get_url (conn);
2964 GST_INFO ("added new client %p ip %s:%d", client, url->host, url->port);
2966 priv->connection = conn;
2973 GST_ERROR ("could not get local address %s", error->message);
2974 g_error_free (error);
2980 * gst_rtsp_client_get_connection:
2981 * @client: a #GstRTSPClient
2983 * Get the #GstRTSPConnection of @client.
2985 * Returns: (transfer none): the #GstRTSPConnection of @client.
2986 * The connection object returned remains valid until the client is freed.
2989 gst_rtsp_client_get_connection (GstRTSPClient * client)
2991 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2993 return client->priv->connection;
2997 * gst_rtsp_client_set_send_func:
2998 * @client: a #GstRTSPClient
2999 * @func: (scope notified): a #GstRTSPClientSendFunc
3000 * @user_data: (closure): user data passed to @func
3001 * @notify: (allow-none): called when @user_data is no longer in use
3003 * Set @func as the callback that will be called when a new message needs to be
3004 * sent to the client. @user_data is passed to @func and @notify is called when
3005 * @user_data is no longer in use.
3007 * By default, the client will send the messages on the #GstRTSPConnection that
3008 * was configured with gst_rtsp_client_attach() was called.
3011 gst_rtsp_client_set_send_func (GstRTSPClient * client,
3012 GstRTSPClientSendFunc func, gpointer user_data, GDestroyNotify notify)
3014 GstRTSPClientPrivate *priv;
3015 GDestroyNotify old_notify;
3018 g_return_if_fail (GST_IS_RTSP_CLIENT (client));
3020 priv = client->priv;
3022 g_mutex_lock (&priv->send_lock);
3023 priv->send_func = func;
3024 old_notify = priv->send_notify;
3025 old_data = priv->send_data;
3026 priv->send_notify = notify;
3027 priv->send_data = user_data;
3028 g_mutex_unlock (&priv->send_lock);
3031 old_notify (old_data);
3035 * gst_rtsp_client_handle_message:
3036 * @client: a #GstRTSPClient
3037 * @message: (transfer none): an #GstRTSPMessage
3039 * Let the client handle @message.
3041 * Returns: a #GstRTSPResult.
3044 gst_rtsp_client_handle_message (GstRTSPClient * client,
3045 GstRTSPMessage * message)
3047 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
3048 g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
3050 switch (message->type) {
3051 case GST_RTSP_MESSAGE_REQUEST:
3052 handle_request (client, message);
3054 case GST_RTSP_MESSAGE_RESPONSE:
3055 handle_response (client, message);
3057 case GST_RTSP_MESSAGE_DATA:
3058 handle_data (client, message);
3067 * gst_rtsp_client_send_message:
3068 * @client: a #GstRTSPClient
3069 * @session: (allow-none) (transfer none): a #GstRTSPSession to send
3070 * the message to or %NULL
3071 * @message: (transfer none): The #GstRTSPMessage to send
3073 * Send a message message to the remote end. @message must be a
3074 * #GST_RTSP_MESSAGE_REQUEST or a #GST_RTSP_MESSAGE_RESPONSE.
3077 gst_rtsp_client_send_message (GstRTSPClient * client, GstRTSPSession * session,
3078 GstRTSPMessage * message)
3080 GstRTSPContext sctx = { NULL }
3082 GstRTSPClientPrivate *priv;
3084 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
3085 g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
3086 g_return_val_if_fail (message->type == GST_RTSP_MESSAGE_REQUEST ||
3087 message->type == GST_RTSP_MESSAGE_RESPONSE, GST_RTSP_EINVAL);
3089 priv = client->priv;
3091 if (!(ctx = gst_rtsp_context_get_current ())) {
3093 ctx->auth = priv->auth;
3094 gst_rtsp_context_push_current (ctx);
3097 ctx->conn = priv->connection;
3098 ctx->client = client;
3099 ctx->session = session;
3101 send_message (client, ctx, message, FALSE);
3104 gst_rtsp_context_pop_current (ctx);
3109 static GstRTSPResult
3110 do_send_message (GstRTSPClient * client, GstRTSPMessage * message,
3111 gboolean close, gpointer user_data)
3113 GstRTSPClientPrivate *priv = client->priv;
3121 /* send the response and store the seq number so we can wait until it's
3122 * written to the client to close the connection */
3124 gst_rtsp_watch_send_message (priv->watch, message,
3125 close ? &priv->close_seq : NULL);
3126 if (ret == GST_RTSP_OK)
3129 if (ret != GST_RTSP_ENOMEM)
3133 if (priv->drop_backlog)
3136 /* queue was full, wait for more space */
3137 GST_DEBUG_OBJECT (client, "waiting for backlog");
3138 ret = gst_rtsp_watch_wait_backlog (priv->watch, &time);
3139 GST_DEBUG_OBJECT (client, "Resend due to backlog full");
3140 } while (ret != GST_RTSP_EINTR);
3147 GST_DEBUG_OBJECT (client, "got error %d", ret);
3152 static GstRTSPResult
3153 message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
3156 return gst_rtsp_client_handle_message (GST_RTSP_CLIENT (user_data), message);
3159 static GstRTSPResult
3160 message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
3162 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3163 GstRTSPClientPrivate *priv = client->priv;
3165 if (priv->close_seq && priv->close_seq == cseq) {
3166 GST_INFO ("client %p: send close message", client);
3167 priv->close_seq = 0;
3168 gst_rtsp_client_close (client);
3174 static GstRTSPResult
3175 closed (GstRTSPWatch * watch, gpointer user_data)
3177 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3178 GstRTSPClientPrivate *priv = client->priv;
3179 const gchar *tunnelid;
3181 GST_INFO ("client %p: connection closed", client);
3183 if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
3184 g_mutex_lock (&tunnels_lock);
3185 /* remove from tunnelids */
3186 g_hash_table_remove (tunnels, tunnelid);
3187 g_mutex_unlock (&tunnels_lock);
3190 gst_rtsp_watch_set_flushing (watch, TRUE);
3191 gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
3196 static GstRTSPResult
3197 error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
3199 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3202 str = gst_rtsp_strresult (result);
3203 GST_INFO ("client %p: received an error %s", client, str);
3209 static GstRTSPResult
3210 error_full (GstRTSPWatch * watch, GstRTSPResult result,
3211 GstRTSPMessage * message, guint id, gpointer user_data)
3213 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3216 str = gst_rtsp_strresult (result);
3218 ("client %p: error when handling message %p with id %d: %s",
3219 client, message, id, str);
3226 remember_tunnel (GstRTSPClient * client)
3228 GstRTSPClientPrivate *priv = client->priv;
3229 const gchar *tunnelid;
3231 /* store client in the pending tunnels */
3232 tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
3233 if (tunnelid == NULL)
3236 GST_INFO ("client %p: inserting tunnel session %s", client, tunnelid);
3238 /* we can't have two clients connecting with the same tunnelid */
3239 g_mutex_lock (&tunnels_lock);
3240 if (g_hash_table_lookup (tunnels, tunnelid))
3241 goto tunnel_existed;
3243 g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
3244 g_mutex_unlock (&tunnels_lock);
3251 GST_ERROR ("client %p: no tunnelid provided", client);
3256 g_mutex_unlock (&tunnels_lock);
3257 GST_ERROR ("client %p: tunnel session %s already existed", client,
3263 static GstRTSPResult
3264 tunnel_lost (GstRTSPWatch * watch, gpointer user_data)
3266 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3267 GstRTSPClientPrivate *priv = client->priv;
3269 GST_WARNING ("client %p: tunnel lost (connection %p)", client,
3272 /* ignore error, it'll only be a problem when the client does a POST again */
3273 remember_tunnel (client);
3279 handle_tunnel (GstRTSPClient * client)
3281 GstRTSPClientPrivate *priv = client->priv;
3282 GstRTSPClient *oclient;
3283 GstRTSPClientPrivate *opriv;
3284 const gchar *tunnelid;
3286 tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
3287 if (tunnelid == NULL)
3290 /* check for previous tunnel */
3291 g_mutex_lock (&tunnels_lock);
3292 oclient = g_hash_table_lookup (tunnels, tunnelid);
3294 if (oclient == NULL) {
3295 /* no previous tunnel, remember tunnel */
3296 g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
3297 g_mutex_unlock (&tunnels_lock);
3299 GST_INFO ("client %p: no previous tunnel found, remembering tunnel (%p)",
3300 client, priv->connection);
3302 /* merge both tunnels into the first client */
3303 /* remove the old client from the table. ref before because removing it will
3304 * remove the ref to it. */
3305 g_object_ref (oclient);
3306 g_hash_table_remove (tunnels, tunnelid);
3307 g_mutex_unlock (&tunnels_lock);
3309 opriv = oclient->priv;
3311 if (opriv->watch == NULL)
3314 GST_INFO ("client %p: found previous tunnel %p (old %p, new %p)", client,
3315 oclient, opriv->connection, priv->connection);
3317 gst_rtsp_connection_do_tunnel (opriv->connection, priv->connection);
3318 gst_rtsp_watch_reset (priv->watch);
3319 gst_rtsp_watch_reset (opriv->watch);
3320 g_object_unref (oclient);
3322 /* the old client owns the tunnel now, the new one will be freed */
3323 g_source_destroy ((GSource *) priv->watch);
3325 gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
3333 GST_ERROR ("client %p: no tunnelid provided", client);
3338 GST_ERROR ("client %p: tunnel session %s was closed", client, tunnelid);
3339 g_object_unref (oclient);
3344 static GstRTSPStatusCode
3345 tunnel_get (GstRTSPWatch * watch, gpointer user_data)
3347 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3349 GST_INFO ("client %p: tunnel get (connection %p)", client,
3350 client->priv->connection);
3352 if (!handle_tunnel (client)) {
3353 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
3356 return GST_RTSP_STS_OK;
3359 static GstRTSPResult
3360 tunnel_post (GstRTSPWatch * watch, gpointer user_data)
3362 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3364 GST_INFO ("client %p: tunnel post (connection %p)", client,
3365 client->priv->connection);
3367 if (!handle_tunnel (client)) {
3368 return GST_RTSP_ERROR;
3374 static GstRTSPResult
3375 tunnel_http_response (GstRTSPWatch * watch, GstRTSPMessage * request,
3376 GstRTSPMessage * response, gpointer user_data)
3378 GstRTSPClientClass *klass;
3380 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3381 klass = GST_RTSP_CLIENT_GET_CLASS (client);
3383 if (klass->tunnel_http_response) {
3384 klass->tunnel_http_response (client, request, response);
3390 static GstRTSPWatchFuncs watch_funcs = {
3399 tunnel_http_response
3403 client_watch_notify (GstRTSPClient * client)
3405 GstRTSPClientPrivate *priv = client->priv;
3407 GST_INFO ("client %p: watch destroyed", client);
3409 g_main_context_unref (priv->watch_context);
3410 priv->watch_context = NULL;
3411 /* remove all sessions and so drop the extra client ref */
3412 gst_rtsp_client_session_filter (client, cleanup_session, NULL);
3413 g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_CLOSED], 0, NULL);
3414 g_object_unref (client);
3418 * gst_rtsp_client_attach:
3419 * @client: a #GstRTSPClient
3420 * @context: (allow-none): a #GMainContext
3422 * Attaches @client to @context. When the mainloop for @context is run, the
3423 * client will be dispatched. When @context is %NULL, the default context will be
3426 * This function should be called when the client properties and urls are fully
3427 * configured and the client is ready to start.
3429 * Returns: the ID (greater than 0) for the source within the GMainContext.
3432 gst_rtsp_client_attach (GstRTSPClient * client, GMainContext * context)
3434 GstRTSPClientPrivate *priv;
3437 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), 0);
3438 priv = client->priv;
3439 g_return_val_if_fail (priv->connection != NULL, 0);
3440 g_return_val_if_fail (priv->watch == NULL, 0);
3442 /* make sure noone will free the context before the watch is destroyed */
3443 priv->watch_context = g_main_context_ref (context);
3445 /* create watch for the connection and attach */
3446 priv->watch = gst_rtsp_watch_new (priv->connection, &watch_funcs,
3447 g_object_ref (client), (GDestroyNotify) client_watch_notify);
3448 gst_rtsp_client_set_send_func (client, do_send_message, priv->watch,
3449 (GDestroyNotify) gst_rtsp_watch_unref);
3451 /* FIXME make this configurable. We don't want to do this yet because it will
3452 * be superceeded by a cache object later */
3453 gst_rtsp_watch_set_send_backlog (priv->watch, 0, 100);
3455 GST_INFO ("client %p: attaching to context %p", client, context);
3456 res = gst_rtsp_watch_attach (priv->watch, context);
3462 * gst_rtsp_client_session_filter:
3463 * @client: a #GstRTSPClient
3464 * @func: (scope call) (allow-none): a callback
3465 * @user_data: user data passed to @func
3467 * Call @func for each session managed by @client. The result value of @func
3468 * determines what happens to the session. @func will be called with @client
3469 * locked so no further actions on @client can be performed from @func.
3471 * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be removed from
3474 * If @func returns #GST_RTSP_FILTER_KEEP, the session will remain in @client.
3476 * If @func returns #GST_RTSP_FILTER_REF, the session will remain in @client but
3477 * will also be added with an additional ref to the result #GList of this
3480 * When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for each session.
3482 * Returns: (element-type GstRTSPSession) (transfer full): a #GList with all
3483 * sessions for which @func returned #GST_RTSP_FILTER_REF. After usage, each
3484 * element in the #GList should be unreffed before the list is freed.
3487 gst_rtsp_client_session_filter (GstRTSPClient * client,
3488 GstRTSPClientSessionFilterFunc func, gpointer user_data)
3490 GstRTSPClientPrivate *priv;
3491 GList *result, *walk, *next;
3492 GHashTable *visited;
3495 g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
3497 priv = client->priv;
3501 visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
3503 g_mutex_lock (&priv->lock);
3505 cookie = priv->sessions_cookie;
3506 for (walk = priv->sessions; walk; walk = next) {
3507 GstRTSPSession *sess = walk->data;
3508 GstRTSPFilterResult res;
3511 next = g_list_next (walk);
3514 /* only visit each session once */
3515 if (g_hash_table_contains (visited, sess))
3518 g_hash_table_add (visited, g_object_ref (sess));
3519 g_mutex_unlock (&priv->lock);
3521 res = func (client, sess, user_data);
3523 g_mutex_lock (&priv->lock);
3525 res = GST_RTSP_FILTER_REF;
3527 changed = (cookie != priv->sessions_cookie);
3530 case GST_RTSP_FILTER_REMOVE:
3531 /* stop watching the session and pretend it went away, if the list was
3532 * changed, we can't use the current list position, try to see if we
3533 * still have the session */
3534 client_unwatch_session (client, sess, changed ? NULL : walk);
3535 cookie = priv->sessions_cookie;
3537 case GST_RTSP_FILTER_REF:
3538 result = g_list_prepend (result, g_object_ref (sess));
3540 case GST_RTSP_FILTER_KEEP:
3547 g_mutex_unlock (&priv->lock);
3550 g_hash_table_unref (visited);