rtsp: move network includes where they are needed
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-server.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  *
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.
8  *
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.
13  *
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <netdb.h>
29 #include <sys/socket.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <arpa/inet.h>
33 #include <sys/ioctl.h>
34
35 #include "rtsp-server.h"
36 #include "rtsp-client.h"
37
38 #define DEFAULT_ADDRESS         "0.0.0.0"
39 /* #define DEFAULT_ADDRESS         "::0" */
40 #define DEFAULT_SERVICE         "8554"
41 #define DEFAULT_BACKLOG         5
42
43 /* Define to use the SO_LINGER option so that the server sockets can be resused
44  * sooner. Disabled for now because it is not very well implemented by various
45  * OSes and it causes clients to fail to read the TEARDOWN response. */
46 #undef USE_SOLINGER
47
48 enum
49 {
50   PROP_0,
51   PROP_ADDRESS,
52   PROP_SERVICE,
53   PROP_BACKLOG,
54
55   PROP_SESSION_POOL,
56   PROP_MEDIA_MAPPING,
57   PROP_LAST
58 };
59
60 G_DEFINE_TYPE (GstRTSPServer, gst_rtsp_server, G_TYPE_OBJECT);
61
62 GST_DEBUG_CATEGORY_STATIC (rtsp_server_debug);
63 #define GST_CAT_DEFAULT rtsp_server_debug
64
65 static void gst_rtsp_server_get_property (GObject * object, guint propid,
66     GValue * value, GParamSpec * pspec);
67 static void gst_rtsp_server_set_property (GObject * object, guint propid,
68     const GValue * value, GParamSpec * pspec);
69 static void gst_rtsp_server_finalize (GObject * object);
70
71 static GstRTSPClient *default_accept_client (GstRTSPServer * server,
72     GIOChannel * channel);
73
74 static void
75 gst_rtsp_server_class_init (GstRTSPServerClass * klass)
76 {
77   GObjectClass *gobject_class;
78
79   gobject_class = G_OBJECT_CLASS (klass);
80
81   gobject_class->get_property = gst_rtsp_server_get_property;
82   gobject_class->set_property = gst_rtsp_server_set_property;
83   gobject_class->finalize = gst_rtsp_server_finalize;
84
85   /**
86    * GstRTSPServer::address
87    *
88    * The address of the server. This is the address where the server will
89    * listen on.
90    */
91   g_object_class_install_property (gobject_class, PROP_ADDRESS,
92       g_param_spec_string ("address", "Address",
93           "The address the server uses to listen on", DEFAULT_ADDRESS,
94           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
95   /**
96    * GstRTSPServer::service
97    *
98    * The service of the server. This is either a string with the service name or
99    * a port number (as a string) the server will listen on.
100    */
101   g_object_class_install_property (gobject_class, PROP_SERVICE,
102       g_param_spec_string ("service", "Service",
103           "The service or port number the server uses to listen on",
104           DEFAULT_SERVICE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
105   /**
106    * GstRTSPServer::backlog
107    *
108    * The backlog argument defines the maximum length to which the queue of
109    * pending connections for the server may grow. If a connection request arrives
110    * when the queue is full, the client may receive an error with an indication of
111    * ECONNREFUSED or, if the underlying protocol supports retransmission, the
112    * request may be ignored so that a later reattempt at  connection succeeds.
113    */
114   g_object_class_install_property (gobject_class, PROP_BACKLOG,
115       g_param_spec_int ("backlog", "Backlog",
116           "The maximum length to which the queue "
117           "of pending connections may grow", 0, G_MAXINT, DEFAULT_BACKLOG,
118           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
119   /**
120    * GstRTSPServer::session-pool
121    *
122    * The session pool of the server. By default each server has a separate
123    * session pool but sessions can be shared between servers by setting the same
124    * session pool on multiple servers.
125    */
126   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
127       g_param_spec_object ("session-pool", "Session Pool",
128           "The session pool to use for client session",
129           GST_TYPE_RTSP_SESSION_POOL,
130           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
131   /**
132    * GstRTSPServer::media-mapping
133    *
134    * The media mapping to use for this server. By default the server has no
135    * media mapping and thus cannot map urls to media streams.
136    */
137   g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
138       g_param_spec_object ("media-mapping", "Media Mapping",
139           "The media mapping to use for client session",
140           GST_TYPE_RTSP_MEDIA_MAPPING,
141           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
142
143   klass->accept_client = default_accept_client;
144
145   GST_DEBUG_CATEGORY_INIT (rtsp_server_debug, "rtspserver", 0, "GstRTSPServer");
146 }
147
148 static void
149 gst_rtsp_server_init (GstRTSPServer * server)
150 {
151   server->address = g_strdup (DEFAULT_ADDRESS);
152   server->service = g_strdup (DEFAULT_SERVICE);
153   server->backlog = DEFAULT_BACKLOG;
154   server->session_pool = gst_rtsp_session_pool_new ();
155   server->media_mapping = gst_rtsp_media_mapping_new ();
156 }
157
158 static void
159 gst_rtsp_server_finalize (GObject * object)
160 {
161   GstRTSPServer *server = GST_RTSP_SERVER (object);
162
163   g_free (server->address);
164   g_free (server->service);
165
166   g_object_unref (server->session_pool);
167   g_object_unref (server->media_mapping);
168 }
169
170 /**
171  * gst_rtsp_server_new:
172  *
173  * Create a new #GstRTSPServer instance.
174  */
175 GstRTSPServer *
176 gst_rtsp_server_new (void)
177 {
178   GstRTSPServer *result;
179
180   result = g_object_new (GST_TYPE_RTSP_SERVER, NULL);
181
182   return result;
183 }
184
185 /**
186  * gst_rtsp_server_set_address:
187  * @server: a #GstRTSPServer
188  * @address: the address
189  *
190  * Configure @server to accept connections on the given address.
191  *
192  * This function must be called before the server is bound.
193  */
194 void
195 gst_rtsp_server_set_address (GstRTSPServer * server, const gchar * address)
196 {
197   g_return_if_fail (GST_IS_RTSP_SERVER (server));
198   g_return_if_fail (address != NULL);
199
200   g_free (server->address);
201   server->address = g_strdup (address);
202 }
203
204 /**
205  * gst_rtsp_server_get_address:
206  * @server: a #GstRTSPServer
207  *
208  * Get the address on which the server will accept connections.
209  *
210  * Returns: the server address. g_free() after usage.
211  */
212 gchar *
213 gst_rtsp_server_get_address (GstRTSPServer * server)
214 {
215   g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
216
217   return g_strdup (server->address);
218 }
219
220 /**
221  * gst_rtsp_server_set_service:
222  * @server: a #GstRTSPServer
223  * @service: the service
224  *
225  * Configure @server to accept connections on the given service.
226  * @service should be a string containing the service name (see services(5)) or
227  * a string containing a port number between 1 and 65535.
228  *
229  * This function must be called before the server is bound.
230  */
231 void
232 gst_rtsp_server_set_service (GstRTSPServer * server, const gchar * service)
233 {
234   g_return_if_fail (GST_IS_RTSP_SERVER (server));
235   g_return_if_fail (service != NULL);
236
237   g_free (server->service);
238   server->service = g_strdup (service);
239 }
240
241 /**
242  * gst_rtsp_server_get_service:
243  * @server: a #GstRTSPServer
244  *
245  * Get the service on which the server will accept connections.
246  *
247  * Returns: the service. use g_free() after usage.
248  */
249 gchar *
250 gst_rtsp_server_get_service (GstRTSPServer * server)
251 {
252   g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
253
254   return g_strdup (server->service);
255 }
256
257 /**
258  * gst_rtsp_server_set_backlog:
259  * @server: a #GstRTSPServer
260  * @backlog: the backlog
261  *
262  * configure the maximum amount of requests that may be queued for the
263  * server.
264  *
265  * This function must be called before the server is bound.
266  */
267 void
268 gst_rtsp_server_set_backlog (GstRTSPServer * server, gint backlog)
269 {
270   g_return_if_fail (GST_IS_RTSP_SERVER (server));
271
272   server->backlog = backlog;
273 }
274
275 /**
276  * gst_rtsp_server_get_backlog:
277  * @server: a #GstRTSPServer
278  *
279  * The maximum amount of queued requests for the server.
280  *
281  * Returns: the server backlog.
282  */
283 gint
284 gst_rtsp_server_get_backlog (GstRTSPServer * server)
285 {
286   g_return_val_if_fail (GST_IS_RTSP_SERVER (server), -1);
287
288   return server->backlog;
289 }
290
291 /**
292  * gst_rtsp_server_set_session_pool:
293  * @server: a #GstRTSPServer
294  * @pool: a #GstRTSPSessionPool
295  *
296  * configure @pool to be used as the session pool of @server.
297  */
298 void
299 gst_rtsp_server_set_session_pool (GstRTSPServer * server,
300     GstRTSPSessionPool * pool)
301 {
302   GstRTSPSessionPool *old;
303
304   g_return_if_fail (GST_IS_RTSP_SERVER (server));
305
306   old = server->session_pool;
307
308   if (old != pool) {
309     if (pool)
310       g_object_ref (pool);
311     server->session_pool = pool;
312     if (old)
313       g_object_unref (old);
314   }
315 }
316
317 /**
318  * gst_rtsp_server_get_session_pool:
319  * @server: a #GstRTSPServer
320  *
321  * Get the #GstRTSPSessionPool used as the session pool of @server.
322  *
323  * Returns: the #GstRTSPSessionPool used for sessions. g_object_unref() after
324  * usage.
325  */
326 GstRTSPSessionPool *
327 gst_rtsp_server_get_session_pool (GstRTSPServer * server)
328 {
329   GstRTSPSessionPool *result;
330
331   g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
332
333   if ((result = server->session_pool))
334     g_object_ref (result);
335
336   return result;
337 }
338
339 /**
340  * gst_rtsp_server_set_media_mapping:
341  * @server: a #GstRTSPServer
342  * @mapping: a #GstRTSPMediaMapping
343  *
344  * configure @mapping to be used as the media mapping of @server.
345  */
346 void
347 gst_rtsp_server_set_media_mapping (GstRTSPServer * server,
348     GstRTSPMediaMapping * mapping)
349 {
350   GstRTSPMediaMapping *old;
351
352   g_return_if_fail (GST_IS_RTSP_SERVER (server));
353
354   old = server->media_mapping;
355
356   if (old != mapping) {
357     if (mapping)
358       g_object_ref (mapping);
359     server->media_mapping = mapping;
360     if (old)
361       g_object_unref (old);
362   }
363 }
364
365
366 /**
367  * gst_rtsp_server_get_media_mapping:
368  * @server: a #GstRTSPServer
369  *
370  * Get the #GstRTSPMediaMapping used as the media mapping of @server.
371  *
372  * Returns: the #GstRTSPMediaMapping of @server. g_object_unref() after
373  * usage.
374  */
375 GstRTSPMediaMapping *
376 gst_rtsp_server_get_media_mapping (GstRTSPServer * server)
377 {
378   GstRTSPMediaMapping *result;
379
380   g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
381
382   if ((result = server->media_mapping))
383     g_object_ref (result);
384
385   return result;
386 }
387
388 static void
389 gst_rtsp_server_get_property (GObject * object, guint propid,
390     GValue * value, GParamSpec * pspec)
391 {
392   GstRTSPServer *server = GST_RTSP_SERVER (object);
393
394   switch (propid) {
395     case PROP_ADDRESS:
396       g_value_take_string (value, gst_rtsp_server_get_address (server));
397       break;
398     case PROP_SERVICE:
399       g_value_take_string (value, gst_rtsp_server_get_service (server));
400       break;
401     case PROP_BACKLOG:
402       g_value_set_int (value, gst_rtsp_server_get_backlog (server));
403       break;
404     case PROP_SESSION_POOL:
405       g_value_take_object (value, gst_rtsp_server_get_session_pool (server));
406       break;
407     case PROP_MEDIA_MAPPING:
408       g_value_take_object (value, gst_rtsp_server_get_media_mapping (server));
409       break;
410     default:
411       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
412   }
413 }
414
415 static void
416 gst_rtsp_server_set_property (GObject * object, guint propid,
417     const GValue * value, GParamSpec * pspec)
418 {
419   GstRTSPServer *server = GST_RTSP_SERVER (object);
420
421   switch (propid) {
422     case PROP_ADDRESS:
423       gst_rtsp_server_set_address (server, g_value_get_string (value));
424       break;
425     case PROP_SERVICE:
426       gst_rtsp_server_set_service (server, g_value_get_string (value));
427       break;
428     case PROP_BACKLOG:
429       gst_rtsp_server_set_backlog (server, g_value_get_int (value));
430       break;
431     case PROP_SESSION_POOL:
432       gst_rtsp_server_set_session_pool (server, g_value_get_object (value));
433       break;
434     case PROP_MEDIA_MAPPING:
435       gst_rtsp_server_set_media_mapping (server, g_value_get_object (value));
436       break;
437     default:
438       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
439   }
440 }
441
442 /* Prepare a server socket for @server and make it listen on the configured port */
443 static gboolean
444 gst_rtsp_server_sink_init_send (GstRTSPServer * server)
445 {
446   int ret, sockfd;
447   struct addrinfo hints;
448   struct addrinfo *result, *rp;
449 #ifdef USE_SOLINGER
450   struct linger linger;
451 #endif
452
453   memset (&hints, 0, sizeof (struct addrinfo));
454   hints.ai_family = AF_UNSPEC;  /* Allow IPv4 or IPv6 */
455   hints.ai_socktype = SOCK_STREAM;      /* stream socket */
456   hints.ai_flags = AI_PASSIVE | AI_CANONNAME;   /* For wildcard IP address */
457   hints.ai_protocol = 0;        /* Any protocol */
458   hints.ai_canonname = NULL;
459   hints.ai_addr = NULL;
460   hints.ai_next = NULL;
461
462   GST_DEBUG_OBJECT (server, "getting address info of %s/%s", server->address,
463       server->service);
464
465   /* resolve the server IP address */
466   if ((ret =
467           getaddrinfo (server->address, server->service, &hints, &result)) != 0)
468     goto no_address;
469
470   /* create server socket, we loop through all the addresses until we manage to
471    * create a socket and bind. */
472   for (rp = result; rp; rp = rp->ai_next) {
473     sockfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
474     if (sockfd == -1) {
475       GST_DEBUG_OBJECT (server, "failed to make socket (%s), try next",
476           g_strerror (errno));
477       continue;
478     }
479
480     /* make address reusable */
481     ret = 1;
482     if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR,
483             (void *) &ret, sizeof (ret)) < 0) {
484       /* warn but try to bind anyway */
485       GST_WARNING_OBJECT (server, "failed to reuse socker (%s)",
486           g_strerror (errno));
487     }
488
489     if (bind (sockfd, rp->ai_addr, rp->ai_addrlen) == 0) {
490       GST_DEBUG_OBJECT (server, "bind on %s", rp->ai_canonname);
491       break;
492     }
493
494     GST_DEBUG_OBJECT (server, "failed to bind socket (%s), try next",
495         g_strerror (errno));
496     close (sockfd);
497   }
498   freeaddrinfo (result);
499
500   if (rp == NULL)
501     goto no_socket;
502
503   server->server_sock.fd = sockfd;
504
505   GST_DEBUG_OBJECT (server, "opened sending server socket with fd %d",
506       server->server_sock.fd);
507
508   /* keep connection alive; avoids SIGPIPE during write */
509   ret = 1;
510   if (setsockopt (server->server_sock.fd, SOL_SOCKET, SO_KEEPALIVE,
511           (void *) &ret, sizeof (ret)) < 0)
512     goto keepalive_failed;
513
514 #ifdef USE_SOLINGER
515   /* make sure socket is reset 5 seconds after close. This ensure that we can
516    * reuse the socket quickly while still having a chance to send data to the
517    * client. */
518   linger.l_onoff = 1;
519   linger.l_linger = 5;
520   if (setsockopt (server->server_sock.fd, SOL_SOCKET, SO_LINGER,
521           (void *) &linger, sizeof (linger)) < 0)
522     goto linger_failed;
523 #endif
524
525   /* set the server socket to nonblocking */
526   fcntl (server->server_sock.fd, F_SETFL, O_NONBLOCK);
527
528   GST_DEBUG_OBJECT (server, "listening on server socket %d with queue of %d",
529       server->server_sock.fd, server->backlog);
530   if (listen (server->server_sock.fd, server->backlog) == -1)
531     goto listen_failed;
532
533   GST_DEBUG_OBJECT (server,
534       "listened on server socket %d, returning from connection setup",
535       server->server_sock.fd);
536
537   GST_INFO_OBJECT (server, "listening on service %s", server->service);
538
539   return TRUE;
540
541   /* ERRORS */
542 no_address:
543   {
544     GST_ERROR_OBJECT (server, "failed to resolve address: %s",
545         gai_strerror (ret));
546     return FALSE;
547   }
548 no_socket:
549   {
550     GST_ERROR_OBJECT (server, "failed to create socket: %s",
551         g_strerror (errno));
552     return FALSE;
553   }
554 keepalive_failed:
555   {
556     GST_ERROR_OBJECT (server, "failed to configure keepalive socket: %s",
557         g_strerror (errno));
558     goto close_error;
559   }
560 #ifdef USE_SOLINGER
561 linger_failed:
562   {
563     GST_ERROR_OBJECT (server, "failed to no linger socket: %s",
564         g_strerror (errno));
565     goto close_error;
566   }
567 #endif
568 listen_failed:
569   {
570     GST_ERROR_OBJECT (server, "failed to listen on socket: %s",
571         g_strerror (errno));
572     goto close_error;
573   }
574 close_error:
575   {
576     if (server->server_sock.fd >= 0) {
577       close (server->server_sock.fd);
578       server->server_sock.fd = -1;
579     }
580     return FALSE;
581   }
582 }
583
584 /* default method for creating a new client object in the server to accept and
585  * handle a client connection on this server */
586 static GstRTSPClient *
587 default_accept_client (GstRTSPServer * server, GIOChannel * channel)
588 {
589   GstRTSPClient *client;
590
591   /* a new client connected, create a session to handle the client. */
592   client = gst_rtsp_client_new ();
593
594   /* set the session pool that this client should use */
595   gst_rtsp_client_set_session_pool (client, server->session_pool);
596   /* set the media mapping that this client should use */
597   gst_rtsp_client_set_media_mapping (client, server->media_mapping);
598
599   /* accept connections for that client, this function returns after accepting
600    * the connection and will run the remainder of the communication with the
601    * client asyncronously. */
602   if (!gst_rtsp_client_accept (client, channel))
603     goto accept_failed;
604
605   return client;
606
607   /* ERRORS */
608 accept_failed:
609   {
610     GST_ERROR_OBJECT (server,
611         "Could not accept client on server socket %d: %s (%d)",
612         server->server_sock.fd, g_strerror (errno), errno);
613     gst_object_unref (client);
614     return NULL;
615   }
616 }
617
618 /**
619  * gst_rtsp_server_io_func:
620  * @channel: a #GIOChannel
621  * @condition: the condition on @source
622  *
623  * A default #GIOFunc that creates a new #GstRTSPClient to accept and handle a
624  * new connection on @channel or @server.
625  *
626  * Returns: TRUE if the source could be connected, FALSE if an error occured.
627  */
628 gboolean
629 gst_rtsp_server_io_func (GIOChannel * channel, GIOCondition condition,
630     GstRTSPServer * server)
631 {
632   GstRTSPClient *client = NULL;
633   GstRTSPServerClass *klass;
634
635   if (condition & G_IO_IN) {
636     klass = GST_RTSP_SERVER_GET_CLASS (server);
637
638     /* a new client connected, create a client object to handle the client. */
639     if (klass->accept_client)
640       client = klass->accept_client (server, channel);
641     if (client == NULL)
642       goto client_failed;
643
644     /* can unref the client now, when the request is finished, it will be
645      * unreffed async. */
646     gst_object_unref (client);
647   } else {
648     GST_WARNING_OBJECT (server, "received unknown event %08x", condition);
649   }
650   return TRUE;
651
652   /* ERRORS */
653 client_failed:
654   {
655     GST_ERROR_OBJECT (server, "failed to create a client");
656     return FALSE;
657   }
658 }
659
660 /**
661  * gst_rtsp_server_get_io_channel:
662  * @server: a #GstRTSPServer
663  *
664  * Create a #GIOChannel for @server.
665  *
666  * Returns: the GIOChannel for @server or NULL when an error occured.
667  */
668 GIOChannel *
669 gst_rtsp_server_get_io_channel (GstRTSPServer * server)
670 {
671   g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
672
673   if (server->io_channel == NULL) {
674     if (!gst_rtsp_server_sink_init_send (server))
675       goto init_failed;
676
677     /* create IO channel for the socket */
678     server->io_channel = g_io_channel_unix_new (server->server_sock.fd);
679   }
680   return server->io_channel;
681
682 init_failed:
683   {
684     GST_ERROR_OBJECT (server, "failed to initialize server");
685     return NULL;
686   }
687 }
688
689 /**
690  * gst_rtsp_server_create_watch:
691  * @server: a #GstRTSPServer
692  *
693  * Create a #GSource for @server. The new source will have a default
694  * #GIOFunc of gst_rtsp_server_io_func().
695  *
696  * Returns: the #GSource for @server or NULL when an error occured.
697  */
698 GSource *
699 gst_rtsp_server_create_watch (GstRTSPServer * server)
700 {
701   g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
702
703   if (server->io_watch == NULL) {
704     GIOChannel *channel;
705
706     channel = gst_rtsp_server_get_io_channel (server);
707     if (channel == NULL)
708       goto no_channel;
709
710     /* create a watch for reads (new connections) and possible errors */
711     server->io_watch = g_io_create_watch (channel, G_IO_IN |
712         G_IO_ERR | G_IO_HUP | G_IO_NVAL);
713
714     /* configure the callback */
715     g_source_set_callback (server->io_watch,
716         (GSourceFunc) gst_rtsp_server_io_func, server, NULL);
717   }
718   return server->io_watch;
719
720 no_channel:
721   {
722     GST_ERROR_OBJECT (server, "failed to create IO channel");
723     return NULL;
724   }
725 }
726
727 /**
728  * gst_rtsp_server_attach:
729  * @server: a #GstRTSPServer
730  * @context: a #GMainContext
731  *
732  * Attaches @server to @context. When the mainloop for @context is run, the
733  * server will be dispatched.
734  *
735  * This function should be called when the server properties and urls are fully
736  * configured and the server is ready to start.
737  *
738  * Returns: the ID (greater than 0) for the source within the GMainContext.
739  */
740 guint
741 gst_rtsp_server_attach (GstRTSPServer * server, GMainContext * context)
742 {
743   guint res;
744   GSource *source;
745
746   g_return_val_if_fail (GST_IS_RTSP_SERVER (server), 0);
747
748   source = gst_rtsp_server_create_watch (server);
749   if (source == NULL)
750     goto no_source;
751
752   res = g_source_attach (source, context);
753
754   return res;
755
756   /* ERRORS */
757 no_source:
758   {
759     GST_ERROR_OBJECT (server, "failed to create watch");
760     return 0;
761   }
762 }