Пример #1
0
/* create a socket for sending to remote machine */
static gboolean
gst_multiudpsink_start (GstBaseSink * bsink)
{
  GstMultiUDPSink *sink;
  GList *clients;
  GstUDPClient *client;
  GError *err = NULL;

  sink = GST_MULTIUDPSINK (bsink);

  sink->external_socket = FALSE;

  if (sink->socket) {
    GST_DEBUG_OBJECT (sink, "using configured socket");
    if (g_socket_get_family (sink->socket) == G_SOCKET_FAMILY_IPV6) {
      sink->used_socket_v6 = G_SOCKET (g_object_ref (sink->socket));
      sink->external_socket = TRUE;
    } else {
      sink->used_socket = G_SOCKET (g_object_ref (sink->socket));
      sink->external_socket = TRUE;
    }
  }

  if (sink->socket_v6) {
    GST_DEBUG_OBJECT (sink, "using configured IPv6 socket");
    g_return_val_if_fail (g_socket_get_family (sink->socket) !=
        G_SOCKET_FAMILY_IPV6, FALSE);

    if (sink->used_socket_v6 && sink->used_socket_v6 != sink->socket_v6) {
      GST_ERROR_OBJECT (sink,
          "Provided different IPv6 sockets in socket and socket-v6 properties");
      return FALSE;
    }

    sink->used_socket_v6 = G_SOCKET (g_object_ref (sink->socket_v6));
    sink->external_socket = TRUE;
  }

  if (!sink->used_socket && !sink->used_socket_v6) {
    GSocketAddress *bind_addr;
    GInetAddress *bind_iaddr;

    if (sink->bind_address) {
      GSocketFamily family;

      bind_iaddr = g_inet_address_new_from_string (sink->bind_address);
      if (!bind_iaddr) {
        GList *results;
        GResolver *resolver;

        resolver = g_resolver_get_default ();
        results =
            g_resolver_lookup_by_name (resolver, sink->bind_address,
            sink->cancellable, &err);
        if (!results) {
          g_object_unref (resolver);
          goto name_resolve;
        }
        bind_iaddr = G_INET_ADDRESS (g_object_ref (results->data));
        g_resolver_free_addresses (results);
        g_object_unref (resolver);
      }

      bind_addr = g_inet_socket_address_new (bind_iaddr, sink->bind_port);
      g_object_unref (bind_iaddr);
      family = g_socket_address_get_family (G_SOCKET_ADDRESS (bind_addr));

      if ((sink->used_socket =
              g_socket_new (family, G_SOCKET_TYPE_DATAGRAM,
                  G_SOCKET_PROTOCOL_UDP, &err)) == NULL) {
        g_object_unref (bind_addr);
        goto no_socket;
      }

      g_socket_bind (sink->used_socket, bind_addr, TRUE, &err);
      if (err != NULL)
        goto bind_error;
    } else {
      /* create sender sockets if none available */
      if ((sink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV4,
                  G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL)
        goto no_socket;

      bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
      bind_addr = g_inet_socket_address_new (bind_iaddr, sink->bind_port);
      g_socket_bind (sink->used_socket, bind_addr, TRUE, &err);
      g_object_unref (bind_addr);
      g_object_unref (bind_iaddr);
      if (err != NULL)
        goto bind_error;

      if ((sink->used_socket_v6 = g_socket_new (G_SOCKET_FAMILY_IPV6,
                  G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP,
                  &err)) == NULL) {
        GST_INFO_OBJECT (sink, "Failed to create IPv6 socket: %s",
            err->message);
        g_clear_error (&err);
      } else {
        bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
        bind_addr = g_inet_socket_address_new (bind_iaddr, sink->bind_port);
        g_socket_bind (sink->used_socket_v6, bind_addr, TRUE, &err);
        g_object_unref (bind_addr);
        g_object_unref (bind_iaddr);
        if (err != NULL)
          goto bind_error;
      }
    }
  }
#ifdef SO_SNDBUF
  {
    socklen_t len;
    gint sndsize, ret;

    len = sizeof (sndsize);
    if (sink->buffer_size != 0) {
      sndsize = sink->buffer_size;

      GST_DEBUG_OBJECT (sink, "setting udp buffer of %d bytes", sndsize);
      /* set buffer size, Note that on Linux this is typically limited to a
       * maximum of around 100K. Also a minimum of 128 bytes is required on
       * Linux. */

      if (sink->used_socket) {
        ret =
            setsockopt (g_socket_get_fd (sink->used_socket), SOL_SOCKET,
            SO_SNDBUF, (void *) &sndsize, len);
        if (ret != 0) {
          GST_ELEMENT_WARNING (sink, RESOURCE, SETTINGS, (NULL),
              ("Could not create a buffer of requested %d bytes, %d: %s",
                  sndsize, ret, g_strerror (errno)));
        }
      }

      if (sink->used_socket_v6) {
        ret =
            setsockopt (g_socket_get_fd (sink->used_socket_v6), SOL_SOCKET,
            SO_SNDBUF, (void *) &sndsize, len);
        if (ret != 0) {
          GST_ELEMENT_WARNING (sink, RESOURCE, SETTINGS, (NULL),
              ("Could not create a buffer of requested %d bytes, %d: %s",
                  sndsize, ret, g_strerror (errno)));
        }
      }
    }

    /* read the value of the receive buffer. Note that on linux this returns 2x the
     * value we set because the kernel allocates extra memory for metadata.
     * The default on Linux is about 100K (which is about 50K without metadata) */
    if (sink->used_socket) {
      ret =
          getsockopt (g_socket_get_fd (sink->used_socket), SOL_SOCKET,
          SO_SNDBUF, (void *) &sndsize, &len);
      if (ret == 0)
        GST_DEBUG_OBJECT (sink, "have UDP buffer of %d bytes", sndsize);
      else
        GST_DEBUG_OBJECT (sink, "could not get UDP buffer size");
    }

    if (sink->used_socket_v6) {
      ret =
          getsockopt (g_socket_get_fd (sink->used_socket_v6), SOL_SOCKET,
          SO_SNDBUF, (void *) &sndsize, &len);
      if (ret == 0)
        GST_DEBUG_OBJECT (sink, "have UDPv6 buffer of %d bytes", sndsize);
      else
        GST_DEBUG_OBJECT (sink, "could not get UDPv6 buffer size");
    }
  }
#endif

#ifdef SO_BINDTODEVICE
  if (sink->multi_iface) {
    if (sink->used_socket) {
      setsockopt (g_socket_get_fd (sink->used_socket), SOL_SOCKET,
          SO_BINDTODEVICE, sink->multi_iface, strlen (sink->multi_iface));
    }
    if (sink->used_socket_v6) {
      setsockopt (g_socket_get_fd (sink->used_socket_v6), SOL_SOCKET,
          SO_BINDTODEVICE, sink->multi_iface, strlen (sink->multi_iface));
    }
  }
#endif

  if (sink->used_socket)
    g_socket_set_broadcast (sink->used_socket, TRUE);
  if (sink->used_socket_v6)
    g_socket_set_broadcast (sink->used_socket_v6, TRUE);

  sink->bytes_to_serve = 0;
  sink->bytes_served = 0;

  gst_multiudpsink_setup_qos_dscp (sink, sink->used_socket);
  gst_multiudpsink_setup_qos_dscp (sink, sink->used_socket_v6);

  /* look for multicast clients and join multicast groups appropriately
     set also ttl and multicast loopback delivery appropriately  */
  for (clients = sink->clients; clients; clients = g_list_next (clients)) {
    client = (GstUDPClient *) clients->data;

    if (!gst_multiudpsink_configure_client (sink, client))
      return FALSE;
  }
  return TRUE;

  /* ERRORS */
no_socket:
  {
    GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL),
        ("Could not create socket: %s", err->message));
    g_clear_error (&err);
    return FALSE;
  }
bind_error:
  {
    GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL),
        ("Failed to bind socket: %s", err->message));
    g_clear_error (&err);
    return FALSE;
  }
name_resolve:
  {
    GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL),
        ("Failed to resolve bind address %s: %s", sink->bind_address,
            err->message));
    g_clear_error (&err);
    return FALSE;
  }
}
Пример #2
0
static GstFlowReturn
gst_udpsrc_create (GstPushSrc * psrc, GstBuffer ** buf)
{
  GstFlowReturn ret = GST_FLOW_OK;
  GstUDPSrc *udpsrc;
  GstBuffer *outbuf;
  gsize offset;
  gssize readsize;
  gssize res;
  gboolean try_again;
  GError *err = NULL;

  udpsrc = GST_UDPSRC_CAST (psrc);

retry:
  /* quick check, avoid going in select when we already have data */
  readsize = g_socket_get_available_bytes (udpsrc->used_socket);
  if (readsize > 0)
    goto no_select;

  do {
    gint64 timeout;

    try_again = FALSE;

    if (udpsrc->timeout)
      timeout = udpsrc->timeout / 1000;
    else
      timeout = -1;

    GST_LOG_OBJECT (udpsrc, "doing select, timeout %" G_GUINT64_FORMAT,
        timeout);

    if (!g_socket_condition_timed_wait (udpsrc->used_socket, G_IO_IN | G_IO_PRI,
            timeout, udpsrc->cancellable, &err)) {
      if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_BUSY)
          || g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
        goto stopped;
      } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
        g_clear_error (&err);
        /* timeout, post element message */
        gst_element_post_message (GST_ELEMENT_CAST (udpsrc),
            gst_message_new_element (GST_OBJECT_CAST (udpsrc),
                gst_structure_new ("GstUDPSrcTimeout",
                    "timeout", G_TYPE_UINT64, udpsrc->timeout, NULL)));
      } else {
        goto select_error;
      }

      try_again = TRUE;
    }
  } while (G_UNLIKELY (try_again));

  /* ask how much is available for reading on the socket, this should be exactly
   * one UDP packet. We will check the return value, though, because in some
   * case it can return 0 and we don't want a 0 sized buffer. */
  readsize = g_socket_get_available_bytes (udpsrc->used_socket);
  if (G_UNLIKELY (readsize < 0))
    goto get_available_error;

  /* If we get here and the readsize is zero, then either select was woken up
   * by activity that is not a read, or a poll error occurred, or a UDP packet
   * was received that has no data. Since we cannot identify which case it is,
   * we handle all of them. This could possibly lead to a UDP packet getting
   * lost, but since UDP is not reliable, we can accept this. */
  if (G_UNLIKELY (!readsize)) {
    /* try to read a packet (and it will be ignored),
     * in case a packet with no data arrived */
    res =
        g_socket_receive_from (udpsrc->used_socket, NULL, NULL,
        0, udpsrc->cancellable, &err);
    if (G_UNLIKELY (res < 0))
      goto receive_error;

    /* poll again */
    goto retry;
  }

no_select:
  GST_LOG_OBJECT (udpsrc, "ioctl says %d bytes available", (int) readsize);

  /* sanity check value from _get_available_bytes(), which might be as
   * large as the kernel-side buffer on some operating systems */
  if (g_socket_get_family (udpsrc->used_socket) == G_SOCKET_FAMILY_IPV4)
    readsize = MIN (MAX_IPV4_UDP_PACKET_SIZE, readsize);

  ret = gst_pad_alloc_buffer (GST_BASE_SRC (udpsrc)->srcpad,
    GST_BUFFER_OFFSET_NONE, readsize, NULL, &outbuf);
  if (GST_FLOW_OK != ret)
    goto alloc_failed;

  offset = 0;

  res =
      g_socket_receive_from (udpsrc->used_socket, NULL,
                  (gchar *) GST_BUFFER_DATA (outbuf),
                  GST_BUFFER_SIZE (outbuf),
                  udpsrc->cancellable, &err);

  if (G_UNLIKELY (res < 0)) {
    /* EHOSTUNREACH for a UDP socket means that a packet sent with udpsink
     * generated a "port unreachable" ICMP response. We ignore that and try
     * again. */
    if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE)) {
      gst_buffer_unref (outbuf);
      outbuf = NULL;
      g_clear_error (&err);
      goto retry;
    }
    goto receive_error;
  }

  /* patch offset and size when stripping off the headers */
  if (G_UNLIKELY (udpsrc->skip_first_bytes != 0)) {
    if (G_UNLIKELY (readsize < udpsrc->skip_first_bytes))
      goto skip_error;

    offset += udpsrc->skip_first_bytes;
    res -= udpsrc->skip_first_bytes;
  }

  GST_BUFFER_OFFSET (outbuf) = offset;
  GST_BUFFER_SIZE (outbuf) = res;

  GST_LOG_OBJECT (udpsrc, "read %d bytes", (int) readsize);

  *buf = GST_BUFFER_CAST (outbuf);

  return ret;

  /* ERRORS */
select_error:
  {
    GST_ELEMENT_ERROR (udpsrc, RESOURCE, READ, (NULL),
        ("select error: %s", err->message));
    g_clear_error (&err);
    return GST_FLOW_ERROR;
  }
stopped:
  {
    GST_DEBUG ("stop called");
    g_clear_error (&err);
    return GST_FLOW_WRONG_STATE;
  }
get_available_error:
  {
    GST_ELEMENT_ERROR (udpsrc, RESOURCE, READ, (NULL),
        ("get available bytes failed"));
    return GST_FLOW_ERROR;
  }
alloc_failed:
  {
    GST_DEBUG ("Allocation failed");
    return ret;
  }
receive_error:
  {
    gst_buffer_unref (outbuf);

    if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_BUSY) ||
        g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
      g_clear_error (&err);
      return GST_FLOW_WRONG_STATE;
    } else {
      GST_ELEMENT_ERROR (udpsrc, RESOURCE, READ, (NULL),
          ("receive error %" G_GSSIZE_FORMAT ": %s", res, err->message));
      g_clear_error (&err);
      return GST_FLOW_ERROR;
    }
  }
skip_error:
  {
    gst_buffer_unref (outbuf);

    GST_ELEMENT_ERROR (udpsrc, STREAM, DECODE, (NULL),
        ("UDP buffer to small to skip header"));
    return GST_FLOW_ERROR;
  }
}
Пример #3
0
/* create a socket for sending to remote machine */
static gboolean
gst_multiudpsink_start (GstBaseSink * bsink)
{
  GstMultiUDPSink *sink;
  GList *clients;
  GstUDPClient *client;
  GError *err = NULL;

  sink = GST_MULTIUDPSINK (bsink);

  if (sink->socket == NULL) {
    GST_DEBUG_OBJECT (sink, "creating sockets");
    /* create sender socket try IP6, fall back to IP4 */
    sink->family = G_SOCKET_FAMILY_IPV6;
    if ((sink->used_socket =
            g_socket_new (G_SOCKET_FAMILY_IPV6,
                G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) {
      sink->family = G_SOCKET_FAMILY_IPV4;
      if ((sink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV4,
                  G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL)
        goto no_socket;
    }

    GST_DEBUG_OBJECT (sink, "have socket");
    sink->external_socket = FALSE;
  } else {
    GST_DEBUG_OBJECT (sink, "using configured socket");
    /* we use the configured socket */
    sink->used_socket = G_SOCKET (g_object_ref (sink->socket));
    sink->family = g_socket_get_family (sink->used_socket);
    sink->external_socket = TRUE;
  }

#ifdef SO_SNDBUF
  {
    socklen_t len;
    gint sndsize, ret;

    len = sizeof (sndsize);
    if (sink->buffer_size != 0) {
      sndsize = sink->buffer_size;

      GST_DEBUG_OBJECT (sink, "setting udp buffer of %d bytes", sndsize);
      /* set buffer size, Note that on Linux this is typically limited to a
       * maximum of around 100K. Also a minimum of 128 bytes is required on
       * Linux. */
      ret =
          setsockopt (g_socket_get_fd (sink->used_socket), SOL_SOCKET,
          SO_SNDBUF, (void *) &sndsize, len);
      if (ret != 0) {
        GST_ELEMENT_WARNING (sink, RESOURCE, SETTINGS, (NULL),
            ("Could not create a buffer of requested %d bytes, %d: %s",
                sndsize, ret, g_strerror (errno)));
      }
    }

    /* read the value of the receive buffer. Note that on linux this returns 2x the
     * value we set because the kernel allocates extra memory for metadata.
     * The default on Linux is about 100K (which is about 50K without metadata) */
    ret =
        getsockopt (g_socket_get_fd (sink->used_socket), SOL_SOCKET, SO_SNDBUF,
        (void *) &sndsize, &len);
    if (ret == 0)
      GST_DEBUG_OBJECT (sink, "have udp buffer of %d bytes", sndsize);
    else
      GST_DEBUG_OBJECT (sink, "could not get udp buffer size");
  }
#endif

  g_socket_set_broadcast (sink->used_socket, TRUE);

  sink->bytes_to_serve = 0;
  sink->bytes_served = 0;

  gst_multiudpsink_setup_qos_dscp (sink);

  /* look for multicast clients and join multicast groups appropriately
     set also ttl and multicast loopback delivery appropriately  */
  for (clients = sink->clients; clients; clients = g_list_next (clients)) {
    client = (GstUDPClient *) clients->data;

    if (!gst_multiudpsink_configure_client (sink, client))
      return FALSE;
  }
  return TRUE;

  /* ERRORS */
no_socket:
  {
    GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL),
        ("Could not create socket: %s", err->message));
    g_clear_error (&err);
    return FALSE;
  }
}