gstreamer_core/libs/gst/net/gstnetclientclock.c
changeset 0 0e761a78d257
child 8 4a7fac7dd34a
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /* GStreamer
       
     2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
       
     3  *                    2005 Wim Taymans <wim@fluendo.com>
       
     4  *                    2005 Andy Wingo <wingo@pobox.com>
       
     5  *
       
     6  * gstnetclientclock.h: clock that synchronizes itself to a time provider over
       
     7  * the network
       
     8  *
       
     9  * This library is free software; you can redistribute it and/or
       
    10  * modify it under the terms of the GNU Library General Public
       
    11  * License as published by the Free Software Foundation; either
       
    12  * version 2 of the License, or (at your option) any later version.
       
    13  *
       
    14  * This library is distributed in the hope that it will be useful,
       
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    17  * Library General Public License for more details.
       
    18  *
       
    19  * You should have received a copy of the GNU Library General Public
       
    20  * License along with this library; if not, write to the
       
    21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    22  * Boston, MA 02111-1307, USA.
       
    23  */
       
    24 /**
       
    25  * SECTION:gstnetclientclock
       
    26  * @short_description: Special clock that synchronizes to a remote time
       
    27  *                     provider.
       
    28  * @see_also: #GstClock, #GstNetTimeProvider, #GstPipeline
       
    29  *
       
    30  * This object implements a custom #GstClock that synchronizes its time
       
    31  * to a remote time provider such as #GstNetTimeProvider.
       
    32  *
       
    33  * A new clock is created with gst_net_client_clock_new() which takes the
       
    34  * address and port of the remote time provider along with a name and
       
    35  * an initial time.
       
    36  *
       
    37  * This clock will poll the time provider and will update its calibration
       
    38  * parameters based on the local and remote observations.
       
    39  *
       
    40  * Various parameters of the clock can be configured with the parent #GstClock
       
    41  * "timeout", "window-size" and "window-threshold" object properties.
       
    42  *
       
    43  * A #GstNetClientClock is typically set on a #GstPipeline with 
       
    44  * gst_pipeline_use_clock().
       
    45  *
       
    46  * Last reviewed on 2005-11-23 (0.9.5)
       
    47  */
       
    48 
       
    49 #ifdef HAVE_CONFIG_H
       
    50 #include "config.h"
       
    51 #endif
       
    52 
       
    53 #include "gstnettimepacket.h"
       
    54 #include "gstnetclientclock.h"
       
    55 
       
    56 #ifdef HAVE_UNISTD_H
       
    57 #include <unistd.h>
       
    58 #endif
       
    59 #ifdef __SYMBIAN32__
       
    60 #include <sys/select.h>
       
    61 #include <glib_global.h>
       
    62 #endif
       
    63 
       
    64 #if defined (_MSC_VER) && _MSC_VER >= 1400
       
    65 #include <io.h>
       
    66 #endif
       
    67 
       
    68 GST_DEBUG_CATEGORY_STATIC (ncc_debug);
       
    69 #define GST_CAT_DEFAULT (ncc_debug)
       
    70 
       
    71 #define DEFAULT_ADDRESS         "127.0.0.1"
       
    72 #define DEFAULT_PORT            5637
       
    73 #define DEFAULT_TIMEOUT         GST_SECOND
       
    74 
       
    75 #ifdef G_OS_WIN32
       
    76 #define getsockname(sock,addr,len) getsockname(sock,addr,(int *)len)
       
    77 #endif
       
    78 
       
    79 enum
       
    80 {
       
    81   PROP_0,
       
    82   PROP_ADDRESS,
       
    83   PROP_PORT,
       
    84 };
       
    85 
       
    86 #define GST_NET_CLIENT_CLOCK_GET_PRIVATE(obj)  \
       
    87   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_NET_CLIENT_CLOCK, GstNetClientClockPrivate))
       
    88 
       
    89 struct _GstNetClientClockPrivate
       
    90 {
       
    91   GstPollFD sock;
       
    92   GstPoll *fdset;
       
    93 };
       
    94 
       
    95 #define _do_init(type) \
       
    96   GST_DEBUG_CATEGORY_INIT (ncc_debug, "netclock", 0, "Network client clock");
       
    97 
       
    98 GST_BOILERPLATE_FULL (GstNetClientClock, gst_net_client_clock,
       
    99     GstSystemClock, GST_TYPE_SYSTEM_CLOCK, _do_init);
       
   100 
       
   101 static void gst_net_client_clock_finalize (GObject * object);
       
   102 static void gst_net_client_clock_set_property (GObject * object, guint prop_id,
       
   103     const GValue * value, GParamSpec * pspec);
       
   104 static void gst_net_client_clock_get_property (GObject * object, guint prop_id,
       
   105     GValue * value, GParamSpec * pspec);
       
   106 
       
   107 static void gst_net_client_clock_stop (GstNetClientClock * self);
       
   108 
       
   109 #ifdef G_OS_WIN32
       
   110 static int
       
   111 inet_aton (const char *c, struct in_addr *paddr)
       
   112 {
       
   113   /* note that inet_addr is deprecated on unix because
       
   114    * inet_addr returns -1 (INADDR_NONE) for the valid 255.255.255.255
       
   115    * address. */
       
   116   paddr->s_addr = inet_addr (c);
       
   117   if (paddr->s_addr == INADDR_NONE)
       
   118     return 0;
       
   119 
       
   120   return 1;
       
   121 }
       
   122 #endif
       
   123 
       
   124 static void
       
   125 gst_net_client_clock_base_init (gpointer g_class)
       
   126 {
       
   127   /* nop */
       
   128 }
       
   129 
       
   130 static void
       
   131 gst_net_client_clock_class_init (GstNetClientClockClass * klass)
       
   132 {
       
   133   GObjectClass *gobject_class;
       
   134 
       
   135   gobject_class = G_OBJECT_CLASS (klass);
       
   136 
       
   137   g_type_class_add_private (klass, sizeof (GstNetClientClockPrivate));
       
   138 
       
   139   gobject_class->finalize = gst_net_client_clock_finalize;
       
   140   gobject_class->get_property = gst_net_client_clock_get_property;
       
   141   gobject_class->set_property = gst_net_client_clock_set_property;
       
   142 
       
   143   g_object_class_install_property (gobject_class, PROP_ADDRESS,
       
   144       g_param_spec_string ("address", "address",
       
   145           "The address of the machine providing a time server, "
       
   146           "as a dotted quad (x.x.x.x)", DEFAULT_ADDRESS, G_PARAM_READWRITE));
       
   147   g_object_class_install_property (gobject_class, PROP_PORT,
       
   148       g_param_spec_int ("port", "port",
       
   149           "The port on which the remote server is listening", 0, G_MAXUINT16,
       
   150           DEFAULT_PORT, G_PARAM_READWRITE));
       
   151 }
       
   152 
       
   153 static void
       
   154 gst_net_client_clock_init (GstNetClientClock * self,
       
   155     GstNetClientClockClass * g_class)
       
   156 {
       
   157   GstClock *clock = GST_CLOCK_CAST (self);
       
   158 
       
   159 #ifdef G_OS_WIN32
       
   160   WSADATA w;
       
   161   int error = WSAStartup (0x0202, &w);
       
   162 
       
   163   if (error) {
       
   164     GST_DEBUG_OBJECT (self, "Error on WSAStartup");
       
   165   }
       
   166   if (w.wVersion != 0x0202) {
       
   167     WSACleanup ();
       
   168   }
       
   169 #endif
       
   170   self->priv = GST_NET_CLIENT_CLOCK_GET_PRIVATE (self);
       
   171 
       
   172   self->port = DEFAULT_PORT;
       
   173   self->address = g_strdup (DEFAULT_ADDRESS);
       
   174 
       
   175   clock->timeout = DEFAULT_TIMEOUT;
       
   176 
       
   177   self->priv->sock.fd = -1;
       
   178   self->thread = NULL;
       
   179 
       
   180   self->servaddr = NULL;
       
   181 }
       
   182 
       
   183 static void
       
   184 gst_net_client_clock_finalize (GObject * object)
       
   185 {
       
   186   GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object);
       
   187 
       
   188   if (self->thread) {
       
   189     gst_net_client_clock_stop (self);
       
   190     g_assert (self->thread == NULL);
       
   191   }
       
   192 
       
   193   if (self->priv->fdset) {
       
   194     gst_poll_free (self->priv->fdset);
       
   195     self->priv->fdset = NULL;
       
   196   }
       
   197 
       
   198   g_free (self->address);
       
   199   self->address = NULL;
       
   200 
       
   201   g_free (self->servaddr);
       
   202   self->servaddr = NULL;
       
   203 
       
   204 #ifdef G_OS_WIN32
       
   205   WSACleanup ();
       
   206 #endif
       
   207 
       
   208   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   209 }
       
   210 
       
   211 static void
       
   212 gst_net_client_clock_set_property (GObject * object, guint prop_id,
       
   213     const GValue * value, GParamSpec * pspec)
       
   214 {
       
   215   GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object);
       
   216 
       
   217   switch (prop_id) {
       
   218     case PROP_ADDRESS:
       
   219       g_free (self->address);
       
   220       if (g_value_get_string (value) == NULL)
       
   221         self->address = g_strdup (DEFAULT_ADDRESS);
       
   222       else
       
   223         self->address = g_strdup (g_value_get_string (value));
       
   224       break;
       
   225     case PROP_PORT:
       
   226       self->port = g_value_get_int (value);
       
   227       break;
       
   228     default:
       
   229       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   230       break;
       
   231   }
       
   232 }
       
   233 
       
   234 static void
       
   235 gst_net_client_clock_get_property (GObject * object, guint prop_id,
       
   236     GValue * value, GParamSpec * pspec)
       
   237 {
       
   238   GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object);
       
   239 
       
   240   switch (prop_id) {
       
   241     case PROP_ADDRESS:
       
   242       g_value_set_string (value, self->address);
       
   243       break;
       
   244     case PROP_PORT:
       
   245       g_value_set_int (value, self->port);
       
   246       break;
       
   247     default:
       
   248       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   249       break;
       
   250   }
       
   251 }
       
   252 
       
   253 static void
       
   254 gst_net_client_clock_observe_times (GstNetClientClock * self,
       
   255     GstClockTime local_1, GstClockTime remote, GstClockTime local_2)
       
   256 {
       
   257   GstClockTime local_avg;
       
   258   gdouble r_squared;
       
   259   GstClock *clock;
       
   260 
       
   261   if (local_2 < local_1)
       
   262     goto bogus_observation;
       
   263 
       
   264   local_avg = (local_2 + local_1) / 2;
       
   265 
       
   266   clock = GST_CLOCK_CAST (self);
       
   267 
       
   268   gst_clock_add_observation (GST_CLOCK (self), local_avg, remote, &r_squared);
       
   269 
       
   270   GST_CLOCK_SLAVE_LOCK (self);
       
   271   if (clock->filling) {
       
   272     self->current_timeout = 0;
       
   273   } else {
       
   274     /* geto formula */
       
   275     self->current_timeout =
       
   276         (1e-3 / (1 - MIN (r_squared, 0.99999))) * GST_SECOND;
       
   277     self->current_timeout = MIN (self->current_timeout, clock->timeout);
       
   278   }
       
   279   GST_CLOCK_SLAVE_UNLOCK (clock);
       
   280 
       
   281   return;
       
   282 
       
   283 bogus_observation:
       
   284   {
       
   285     GST_WARNING_OBJECT (self, "time packet receive time < send time (%"
       
   286         GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")", GST_TIME_ARGS (local_1),
       
   287         GST_TIME_ARGS (local_2));
       
   288     return;
       
   289   }
       
   290 }
       
   291 
       
   292 static gint
       
   293 gst_net_client_clock_do_select (GstNetClientClock * self)
       
   294 {
       
   295   while (TRUE) {
       
   296     GstClockTime diff;
       
   297     gint ret;
       
   298 
       
   299     GST_LOG_OBJECT (self, "doing select");
       
   300 
       
   301     diff = gst_clock_get_internal_time (GST_CLOCK (self));
       
   302     ret = gst_poll_wait (self->priv->fdset, self->current_timeout);
       
   303     diff = gst_clock_get_internal_time (GST_CLOCK (self)) - diff;
       
   304 
       
   305     if (diff > self->current_timeout)
       
   306       self->current_timeout = 0;
       
   307     else
       
   308       self->current_timeout -= diff;
       
   309 
       
   310     GST_LOG_OBJECT (self, "select returned %d", ret);
       
   311 
       
   312     if (ret < 0 && errno != EBUSY) {
       
   313       if (errno != EAGAIN && errno != EINTR)
       
   314         goto select_error;
       
   315       else
       
   316         continue;
       
   317     } else {
       
   318       return ret;
       
   319     }
       
   320 
       
   321     g_assert_not_reached ();
       
   322 
       
   323     /* log errors and keep going */
       
   324   select_error:
       
   325     {
       
   326       GST_WARNING_OBJECT (self, "select error %d: %s (%d)", ret,
       
   327           g_strerror (errno), errno);
       
   328       continue;
       
   329     }
       
   330   }
       
   331 
       
   332   g_assert_not_reached ();
       
   333   return -1;
       
   334 }
       
   335 
       
   336 static gpointer
       
   337 gst_net_client_clock_thread (gpointer data)
       
   338 {
       
   339   GstNetClientClock *self = data;
       
   340   struct sockaddr_in tmpaddr;
       
   341   socklen_t len;
       
   342   GstNetTimePacket *packet;
       
   343   gint ret;
       
   344   GstClock *clock = data;
       
   345 
       
   346   while (TRUE) {
       
   347     ret = gst_net_client_clock_do_select (self);
       
   348 
       
   349     if (ret < 0 && errno == EBUSY) {
       
   350       GST_LOG_OBJECT (self, "stop");
       
   351       goto stopped;
       
   352     } else if (ret == 0) {
       
   353       /* timed out, let's send another packet */
       
   354       GST_DEBUG_OBJECT (self, "timed out");
       
   355 
       
   356       packet = gst_net_time_packet_new (NULL);
       
   357 
       
   358       packet->local_time = gst_clock_get_internal_time (GST_CLOCK (self));
       
   359 
       
   360       GST_DEBUG_OBJECT (self, "sending packet, local time = %" GST_TIME_FORMAT,
       
   361           GST_TIME_ARGS (packet->local_time));
       
   362       gst_net_time_packet_send (packet, self->priv->sock.fd,
       
   363           (struct sockaddr *) self->servaddr, sizeof (struct sockaddr_in));
       
   364 
       
   365       g_free (packet);
       
   366 
       
   367       /* reset timeout */
       
   368       self->current_timeout = clock->timeout;
       
   369       continue;
       
   370     } else if (gst_poll_fd_can_read (self->priv->fdset, &self->priv->sock)) {
       
   371       /* got data in */
       
   372       GstClockTime new_local = gst_clock_get_internal_time (GST_CLOCK (self));
       
   373 
       
   374       len = sizeof (struct sockaddr);
       
   375       packet = gst_net_time_packet_receive (self->priv->sock.fd,
       
   376           (struct sockaddr *) &tmpaddr, &len);
       
   377 
       
   378       if (!packet)
       
   379         goto receive_error;
       
   380 
       
   381       GST_LOG_OBJECT (self, "got packet back");
       
   382       GST_LOG_OBJECT (self, "local_1 = %" GST_TIME_FORMAT,
       
   383           GST_TIME_ARGS (packet->local_time));
       
   384       GST_LOG_OBJECT (self, "remote = %" GST_TIME_FORMAT,
       
   385           GST_TIME_ARGS (packet->remote_time));
       
   386       GST_LOG_OBJECT (self, "local_2 = %" GST_TIME_FORMAT,
       
   387           GST_TIME_ARGS (new_local));
       
   388 
       
   389       /* observe_times will reset the timeout */
       
   390       gst_net_client_clock_observe_times (self, packet->local_time,
       
   391           packet->remote_time, new_local);
       
   392 
       
   393       g_free (packet);
       
   394       continue;
       
   395     } else {
       
   396       GST_WARNING_OBJECT (self, "unhandled select return state?");
       
   397       continue;
       
   398     }
       
   399 
       
   400     g_assert_not_reached ();
       
   401 
       
   402   stopped:
       
   403     {
       
   404       GST_DEBUG_OBJECT (self, "shutting down");
       
   405       /* socket gets closed in _stop() */
       
   406       return NULL;
       
   407     }
       
   408   receive_error:
       
   409     {
       
   410       GST_WARNING_OBJECT (self, "receive error");
       
   411       continue;
       
   412     }
       
   413 
       
   414     g_assert_not_reached ();
       
   415 
       
   416   }
       
   417 
       
   418   g_assert_not_reached ();
       
   419 
       
   420   return NULL;
       
   421 }
       
   422 
       
   423 static gboolean
       
   424 gst_net_client_clock_start (GstNetClientClock * self)
       
   425 {
       
   426   struct sockaddr_in servaddr, myaddr;
       
   427   socklen_t len;
       
   428   gint ret;
       
   429   GError *error;
       
   430 
       
   431   g_return_val_if_fail (self->address != NULL, FALSE);
       
   432   g_return_val_if_fail (self->servaddr == NULL, FALSE);
       
   433 
       
   434   if ((ret = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
       
   435     goto no_socket;
       
   436 
       
   437   self->priv->sock.fd = ret;
       
   438 
       
   439   len = sizeof (myaddr);
       
   440 #ifndef __SYMBIAN32__
       
   441   ret = getsockname (self->priv->sock.fd, (struct sockaddr *) &myaddr, &len);
       
   442   if (ret < 0)
       
   443     goto getsockname_error;
       
   444 #endif
       
   445   memset (&servaddr, 0, sizeof (servaddr));
       
   446   servaddr.sin_family = AF_INET;        /* host byte order */
       
   447   servaddr.sin_port = htons (self->port);       /* short, network byte order */
       
   448 
       
   449   GST_DEBUG_OBJECT (self, "socket opened on UDP port %hd",
       
   450       ntohs (servaddr.sin_port));
       
   451 
       
   452    #ifndef __SYMBIAN32__
       
   453    if (!inet_aton (self->address, &servaddr.sin_addr))
       
   454       goto bad_address;
       
   455    #else
       
   456    if ((servaddr.sin_addr.s_addr=inet_addr(self->address))==-1)
       
   457    goto bad_address; 
       
   458   #endif//__SYMBIAN32__
       
   459   self->servaddr = g_malloc (sizeof (struct sockaddr_in));
       
   460   memcpy (self->servaddr, &servaddr, sizeof (servaddr));
       
   461 
       
   462   GST_DEBUG_OBJECT (self, "will communicate with %s:%d", self->address,
       
   463       self->port);
       
   464 
       
   465   gst_poll_add_fd (self->priv->fdset, &self->priv->sock);
       
   466   gst_poll_fd_ctl_read (self->priv->fdset, &self->priv->sock, TRUE);
       
   467 
       
   468   self->thread = g_thread_create (gst_net_client_clock_thread, self, TRUE,
       
   469       &error);
       
   470   if (!self->thread)
       
   471     goto no_thread;
       
   472 
       
   473   return TRUE;
       
   474 
       
   475   /* ERRORS */
       
   476 no_socket:
       
   477   {
       
   478     GST_ERROR_OBJECT (self, "socket failed %d: %s (%d)", ret,
       
   479         g_strerror (errno), errno);
       
   480     return FALSE;
       
   481   }
       
   482 getsockname_error:
       
   483   {
       
   484     GST_ERROR_OBJECT (self, "getsockname failed %d: %s (%d)", ret,
       
   485         g_strerror (errno), errno);
       
   486     close (self->priv->sock.fd);
       
   487     self->priv->sock.fd = -1;
       
   488     return FALSE;
       
   489   }
       
   490 bad_address:
       
   491   {
       
   492     GST_ERROR_OBJECT (self, "inet_aton failed %d: %s (%d)", ret,
       
   493         g_strerror (errno), errno);
       
   494     close (self->priv->sock.fd);
       
   495     self->priv->sock.fd = -1;
       
   496     return FALSE;
       
   497   }
       
   498 no_thread:
       
   499   {
       
   500     GST_ERROR_OBJECT (self, "could not create thread: %s", error->message);
       
   501     gst_poll_remove_fd (self->priv->fdset, &self->priv->sock);
       
   502     close (self->priv->sock.fd);
       
   503     self->priv->sock.fd = -1;
       
   504     g_free (self->servaddr);
       
   505     self->servaddr = NULL;
       
   506     g_error_free (error);
       
   507     return FALSE;
       
   508   }
       
   509 }
       
   510 
       
   511 static void
       
   512 gst_net_client_clock_stop (GstNetClientClock * self)
       
   513 {
       
   514   gst_poll_set_flushing (self->priv->fdset, TRUE);
       
   515   g_thread_join (self->thread);
       
   516   self->thread = NULL;
       
   517 
       
   518   if (self->priv->sock.fd != -1) {
       
   519     gst_poll_remove_fd (self->priv->fdset, &self->priv->sock);
       
   520     close (self->priv->sock.fd);
       
   521     self->priv->sock.fd = -1;
       
   522   }
       
   523 }
       
   524 
       
   525 /**
       
   526  * gst_net_client_clock_new:
       
   527  * @name: a name for the clock
       
   528  * @remote_address: the address of the remote clock provider
       
   529  * @remote_port: the port of the remote clock provider
       
   530  * @base_time: initial time of the clock
       
   531  *
       
   532  * Create a new #GstNetClientClock that will report the time
       
   533  * provided by the #GstNetClockProvider on @remote_address and 
       
   534  * @remote_port.
       
   535  *
       
   536  * Returns: a new #GstClock that receives a time from the remote
       
   537  * clock.
       
   538  */
       
   539 #ifdef __SYMBIAN32__
       
   540 EXPORT_C
       
   541 #endif
       
   542 
       
   543 GstClock *
       
   544 gst_net_client_clock_new (gchar * name, const gchar * remote_address,
       
   545     gint remote_port, GstClockTime base_time)
       
   546 {
       
   547   GstNetClientClock *ret;
       
   548   GstClockTime internal;
       
   549 
       
   550   g_return_val_if_fail (remote_address != NULL, NULL);
       
   551   g_return_val_if_fail (remote_port > 0, NULL);
       
   552   g_return_val_if_fail (remote_port <= G_MAXUINT16, NULL);
       
   553   g_return_val_if_fail (base_time != GST_CLOCK_TIME_NONE, NULL);
       
   554 
       
   555   ret = g_object_new (GST_TYPE_NET_CLIENT_CLOCK, "address", remote_address,
       
   556       "port", remote_port, NULL);
       
   557 
       
   558   /* gst_clock_get_time() values are guaranteed to be increasing. because no one
       
   559    * has called get_time on this clock yet we are free to adjust to any value
       
   560    * without worrying about worrying about MAX() issues with the clock's
       
   561    * internal time.
       
   562    */
       
   563 
       
   564   /* update our internal time so get_time() give something around base_time.
       
   565      assume that the rate is 1 in the beginning. */
       
   566   internal = gst_clock_get_internal_time (GST_CLOCK (ret));
       
   567   gst_clock_set_calibration (GST_CLOCK (ret), internal, base_time, 1, 1);
       
   568 
       
   569   {
       
   570     GstClockTime now = gst_clock_get_time (GST_CLOCK (ret));
       
   571 
       
   572     if (now < base_time || now > base_time + GST_SECOND)
       
   573       g_warning ("unable to set the base time, expect sync problems!");
       
   574   }
       
   575 
       
   576   if ((ret->priv->fdset = gst_poll_new (TRUE)) == NULL)
       
   577     goto no_fdset;
       
   578 
       
   579   if (!gst_net_client_clock_start (ret))
       
   580     goto failed_start;
       
   581 
       
   582   /* all systems go, cap'n */
       
   583   return (GstClock *) ret;
       
   584 
       
   585 no_fdset:
       
   586   {
       
   587     GST_ERROR_OBJECT (ret, "could not create an fdset: %s (%d)",
       
   588         g_strerror (errno), errno);
       
   589     gst_object_unref (ret);
       
   590     return NULL;
       
   591   }
       
   592 failed_start:
       
   593   {
       
   594     /* already printed a nice error */
       
   595     gst_object_unref (ret);
       
   596     return NULL;
       
   597   }
       
   598 }